【问题标题】:Django Tastypie: Imlementing Many To Many "through" relationshipsDjango Tastypie:实现多对多“通过”关系
【发布时间】:2014-03-24 04:40:24
【问题描述】:

我对这个问题进行了很多搜索,并在 Stack Overflow 上解决了一堆相关问题,但似乎没有关于如何“通过”中间模型实现多对多关系的明确答案(或者我可能错过了)。

我有一个名为 Sample 的模型,它与 Region 具有多对多的关系。有一个连接两者的中间模型,名为 SampleRegion。我目前没有保存任何关于中间模型的额外信息,但我可能会在未来保存。

这是我的模型:

class Sample(models.Model):
    sample_id = models.BigIntegerField(primary_key=True)
    description = models.TextField(blank=True)
    objects = models.GeoManager()
    regions = ManyToManyField(Region, through='SampleRegion')
    class Meta:
        db_table = u'samples'
    def save(self, **kwargs):
        # Assign a sample ID only for create requests
        if self.sample_id is None:
            try: id = Sample.objects.latest('sample_id').sample_id + 1
            except Sample.DoesNotExist: id = 1
            self.sample_id = id
        super(Sample, self).save

class Region(models.Model):
    name = models.CharField(max_length=100, unique=True)
    def __unicode__(self):
        return self.name
    class Meta:
        db_table = u'regions'

class SampleRegion(models.Model):
    sample = models.ForeignKey('Sample')
    region = models.ForeignKey(Region)
    class Meta:
        unique_together = (('sample', 'region'),)
        db_table = u'sample_regions'

这是我用来编写资源的一种方法。这是不正确的,我无法找出正确的方法:

class SampleResource(ModelResource):
    regions = fields.ToManyField("tastyapi.resources.RegionResource",
                                  "regions")
    class Meta:
        queryset = models.Sample.objects.all()
        allowed_methods = ['get', 'post', 'put', 'delete']
        authentication = ApiKeyAuthentication()
        authorization = ObjectAuthorization('tastyapi', 'sample')
        excludes = ['user', 'collector']
        filtering = {
                'version': ALL,
                'sesar_number': ALL
                }
        validation = VersionValidation(queryset, 'sample_id')

    def hydrate_regions(self, bundle): 
        # code to create a new SampleRegion object by getting a list of 
        # regions from bundle.data['regions']

class RegionResource(ModelResource):
    class Meta:
        queryset = models.Region.objects.all()
        allowed_methods = ['get']
        resource_name = "region"
        filtering = {
                'region': ALL,
                }

这就是我发出 POST 请求的方式:

post_data = {
    'regions': ["/tastyapi/v1/region/2/"],
    'description': 'Created by a test case',
}

client.post('/tastyapi/v1/sample/', data = post_data,
            authentication = credentials, format = 'json')

此请求无效,因为此时 bundle.data['regions'] 为 None 它到达hydrate_regions

有人对我应该如何实施此方案有任何建议吗?

【问题讨论】:

    标签: django tastypie


    【解决方案1】:

    我几天前就知道了。这是我发现的...

    如果您没有显式创建中间表,Django 会为您创建 M2M 关系。但是,如果您显式使用中间表,则您有责任在中间表中创建记录。为了让它在 Tastypie 中工作,我必须重写 save_m2m 方法,以在中间表中显式创建一条记录,将我刚刚创建的样本和现有区域链接起来。

    这就是我的resources.py 的相关部分现在的样子:

    class SampleResource(ModelResource):
        regions = fields.ToManyField("tastyapi.resources.RegionResource",
                                     "regions")
    
        class Meta:
            queryset = models.Sample.objects.all()
            allowed_methods = ['get', 'post', 'put', 'delete']
            authentication = ApiKeyAuthentication()
            authorization = ObjectAuthorization('tastyapi', 'sample')
            excludes = ['user', 'collector']
            filtering = {
                    'regions': ALL_WITH_RELATIONS,
                    }
            validation = VersionValidation(queryset, 'sample_id')
    
        def save_m2m(self, bundle):
            for field_name, field_object in self.fields.items():
                if not getattr(field_object, 'is_m2m', False):
                    continue
    
                if not field_object.attribute:
                    continue
    
                for field in bundle.data[field_name]:
                    kwargs = {'sample': models.Sample.objects.get(pk=bundle.obj.sample_id),
                              'region': field.obj}
    
                    try: SampleRegion.objects.get_or_create(**kwargs)
                    except IntegrityError: continue
    
    class RegionResource(BaseResource):
        class Meta:
            queryset = models.Region.objects.all()
            authentication = ApiKeyAuthentication()
            allowed_methods = ['get']
            resource_name = "region"
            filtering = { 'region': ALL }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-04-06
      • 1970-01-01
      • 2012-02-29
      • 2016-04-18
      • 2015-02-15
      • 2012-11-25
      • 1970-01-01
      相关资源
      最近更新 更多