Django Tastypie: воплощение многих сквозных отношений

Я много искал об этой проблеме и перебрал кучу связанных вопросов по переполнению стека, но, похоже, нет однозначного ответа о том, как реализовать отношения "многие ко многим" "через" промежуточную модель (или, может быть, Я скучаю по этому).

У меня есть модель с именем 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['region'] является None к тому времени, когда он достигает hydrate_regions,

Кто-нибудь есть какие-либо советы о том, как мне следует реализовать этот сценарий?

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 }
Другие вопросы по тегам