Django Tastypie: Moving Many of Many “Across” Relationships

I searched this issue many times and went through a bunch of related stack overflow questions, but there seems to be no definitive answer on how to implement many-to-many relationships through an intermediate model (or maybe I skipped this).

I have a model called Sample that has many-to-many relationships with Region. There is an intermediate model that connects two called SampleRegion. I do not currently save additional information about the intermediate model, but I could in the future.

Here are my models:

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 resources. This is not correct, and I cannot figure out how to do this correctly:

 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, } 

This is how I make 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') 

This query does not work because bundle.data ['regions'] is None by the time it reaches hydrate_regions .

Does anyone have any recommendations as to how I should execute this script?

+6
source share
1 answer

I thought about it a couple of days ago. Here is what I found ...

Django takes care of creating M2M relationships for you, unless you explicitly create a staging table. However, if you explicitly use the staging table, you are responsible for creating the record in the staging table. To make this work in Tastypie, I had to override the save_m2m method to explicitly create an entry in the staging table linking my sample that I just created and the existing area.

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

 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 } 
+7
source

Source: https://habr.com/ru/post/910999/


All Articles