Django Deliciouspie:实现“通过"的多对多关系 [英] Django Tastypie: Imlementing Many To Many "through" relationships

查看:54
本文介绍了Django Deliciouspie:实现“通过"的多对多关系的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

关于这个问题,我进行了很多搜索,并在Stack Overflow上遇到了一系列相关问题,但是对于如何通过"中间模型实现多对多关系,似乎没有明确的答案.(或者我错过了).

I have searched about this issue a lot and gone through a bunch of related questions on Stack Overflow, but there doesn't seem to be a definitive answer about how to implement many-to-many relationships "through" an intermediate model (or maybe I missed it).

我有一个名为Sample的模型,该模型与Region具有多对多关系.有一个将两者联系在一起的中间模型,名为SampleRegion.我目前没有在中间模型上保存任何其他信息,但将来可能会保存.

I have a model named Sample which has a many-to-many relationship with Region. There is an intermediate model which connects the two, named SampleRegion. I am currently not saving any extra information on the intermediate model, but I might in the future.

这是我的模特:

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'

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

And here is one approach I took to write the resources. It's not correct, and I am not able to figure out the right way of doing it:

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请求的方式:

This is how I making a POST request:

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 .

This request doesn't work because bundle.data['regions'] is None by the time it reaches hydrate_regions.

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

Does anybody have any advice on how I should go about implementing this scenario?

推荐答案

几天前我就想到了这一点.这是我发现的...

I figured this out a couple of days ago. Here's what I found...

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

Django takes care of creating M2M relationships for you if you are not explicitly creating an intermediate table. However, if you are explicitly using an intermediate table, then you are responsible for creating a record in the intermediate table. To get this working in Tastypie, I had to override the save_m2m method to explicitly create a record in the intermediate table linking my the sample I have just created and an existing region.

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

This is how the relevant part of my resources.py looks like now:

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 }

这篇关于Django Deliciouspie:实现“通过"的多对多关系的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆