I have this model in models.py:
class Foo(models.Model):
bar = models.ManyToManyField('self')
How can I serialize it?
I was using one of solutions from this topic: Django rest framework nested self-referential objects But none of them working with M2M, either causes in infinity cycle of serialization. Solution where I'm using a row of serializers works good except it's limited by copy&paste (sounds really sad :) )
Generic model mostly looks like a comment's tree. I hope such a problem was already solved before but haven't found how yet.
I don't think you will get a straight solution to this, so try this
class FooBaseSerializer(serializers.ModelSerializer):
class Meta:
fields = '__all__'
model = Foo
class FooSerializer(FooBaseSerializer):
bar = FooBaseSerializer(many=True)
NOTE: As a Software Engineer, I love to use Architectures and I have deeply worked on Layered Approach for Development so I am gonna be Answering it with Respect to Tiers.
I would suggest the use of a Bridge Table for M2M relationships to Normalize Stuff. Anyhow serializing many of the Items would require to
serializer = SerializerNameGoesHere(AllFilteredObjectsfromModel, many=True)
would serializer m2m.
Here's an Example in Layered Approach!
As i understood the Issue, Here's the Solution
models.py
class Member(models.Model):
member_id = models.AutoField(primary_key=True)
member_name = models.CharField(max_length =
class Group(models.Model):
group_id = models.AutoField(primary_key=True)
group_name = models.CharField(max_length = 20)
fk_member_id = models.ForeignKey('Member', models.DO_NOTHING,
db_column='fk_member_id', blank=True, null=True)
class Membership(models.Model):
membershipid = models.AutoField(primary_key=True)
fk_group_id = models.ForeignKey('Group', models.DO_NOTHING,
db_column='fk_member_id', blank=True, null=True)
join_date = models.DateTimeField()
serializers.py
import serializer
class AllSerializer(serializer.Serializer):
group_id = serializer.IntegerField()
group_name = serializer.CharField(max_length = 20)
join_date = serializer.DateTimeField()
CustomModels.py
imports...
class AllDataModel():
group_id = ""
group_name = ""
join_date = ""
BusinessLogic.py
imports ....
class getdata(memberid):
alldataDict = {}
dto = []
Member = models.Members.objects.get(member_id=memberid) #or use filter for Name
alldataDict["MemberId"] = Member.member_id
alldataDict["MemberName"] = Member.member_name
Groups = models.Group.objects.filter(fk_member_id=Member)
for item in Groups:
Custommodel = CustomModels.AllDataModel()
Custommodel.group_id = item.group_id
Custommodel.group_name = item.group_name
Membership = models.Membership.objects.get(fk_group_id=item.group_id)
Custommodel.join_date = Membership.join_date
dto.append(Custommodel)
serializer = AllSerializer(dto,many=True)
alldataDict.update(serializer.data)
return alldataDict
Related
Currently experiencing a problem where my models aren't joining on the foreign key properly,
Doctors can only have one toolset, essentially I need to return [DoctorName, Category, Type, Subtype, Syringe, Bandage]
ToolSetCategoryID, ToolSetTypeID, ToolSetSubTypeID all join on each other's respective tables and all join back to ToolSets
an example of the models would be:
class Doctor(models.Model):
DoctorID = models.AutoField(db_column='DoctorID', primary_key=True)
ToolSetID = models.ForeignKey("ToolSets", db_column='ToolSetID', on_delete=models.CASCADE)
DoctorName = models.CharField(db_column='DoctorName', max_length=100)
class Meta:
managed = False
db_table = 'tblDoctors'
default_permissions = ['add','edit','delete']
class ToolSetCategories(models.Model):
ToolSetCategoryID = models.AutoField(db_column='ToolSetCategoryID', primary_key=True)
Category = models.CharField(db_column='Category', max_length=100)
class Meta:
managed = False
db_table = 'tblToolSetCategories'
default_permissions = ['add','edit','delete']
class ToolSetTypes(models.Model):
ToolSetTypeID = models.AutoField(db_column='ToolSetTypeID', primary_key=True)
ToolSetCategoryID = models.ForeignKey("ToolSetCategories",db_column='ToolSetCategoryID',on_delete=models.CASCADE)
Type = models.CharField(db_column='Type', max_length=100)
class Meta:
managed = False
db_table = 'tblToolSetTypes'
default_permissions = ['add','edit','delete']
class ToolSetSubTypes(models.Model):
ToolSetSubTypeID = models.AutoField(db_column='ToolSetSubTypeID', primary_key=True)
ToolSetTypeID = models.ForeignKey("ToolSetTypes",db_column='ToolSetTypeID',on_delete=models.CASCADE)
ToolSetCategoryID = models.ForeignKey("ToolSetCategories",db_column='ToolSetCategoryID',on_delete=models.CASCADE)
SubType = models.CharField(db_column='SubType', max_length=100)
class Meta:
managed = False
db_table = 'tblToolSetSubTypes'
default_permissions = ['add','edit','delete']
class ToolSets(models.Model):
ToolSetID = models.AutoField(db_column='ToolSetID', primary_key=True)
ToolSetCategoryID = models.ForeignKey("ToolSetCategories",db_column='ToolSetCategoryID',on_delete=models.CASCADE)
ToolSetTypeID = models.ForeignKey("ToolSetTypes",db_column='ToolSetTypeID',on_delete=models.CASCADE)
ToolSetSubTypeID = models.ForeignKey("ToolSetSubTypes",db_column='ToolSetSubTypeID',on_delete=models.CASCADE)
Syringe = models.CharField(db_column='Syringe', max_length=100)
Bandage = models.CharField(db_column='Bandage', max_length=100)
class Meta:
managed = False
db_table = 'tblToolSets'
default_permissions = ['add','edit','delete']
I've found either the fix or a workaround.
I had to filter the conditions I would've used in a join instead of using a join.
You can use Django's built in function F in order to reference a fields dynamic data to filter against (such as filtering field data against another field's data).
Not the best looking solution but is working for now, if anyone can find a more efficient way of doing this please post an answer below.
from django.db.models import F
Doctor.objects.using("test").filter(DoctorID=DoctorID).filter(
ToolSetID__ToolSetTypeID__ToolSetCategoryID=F("ToolSetID__ToolSetCategoryID"),
ToolSetID__ToolSetSubTypeID__ToolSetTypeID=F("ToolSetID__ToolSetTypeID"),
ToolSetID__ToolSetSubTypeID__ToolSetCategoryID=F("ToolSetID__ToolSetCategoryID"),
)
I have the below models:
# Child
class Media (models.Model):
id = models.BigAutoField(primary_key=True)
name = models.CharField(max_length=128,unique=True)
file = models.FileField()
enabled = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
# Parent
class Gallery(models.Model):
id = models.BigAutoField(primary_key=True)
name = models.CharField(max_length=128,unique=True)
description = models.TextField(max_length=254,null=True)
medias = models.ManyToManyField(Media,related_name='medias')
enabled = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
verbose_name = "gallery"
verbose_name_plural = "galleries"
def __str__(self):
return self.name
I would like to be able to sort the child table by setting it in junction table; therefore not affecting the child table. I'm thinking of setting position field in junction table, is manually adding it in DB the only way of doing that? I'm fairly new to Django and I'm sorry in advance if this happens to be just a basic question.
Usual ManyToMany doesn't work here, because association table should contain order. So, this case you have to bring through to the mix:
class Gallery(models.Model):
medias = models.ManyToManyField('Media',related_name='medias', through='ChildGallery')
Where ChildGallery is a join model:
class ChildGallery(models.Model):
child = models.ForeignKey(Child)
gallery = models.ForeignKey(Gallery)
order = models.IntegerField()
class Meta:
ordering = ['order']
I don't know how you would like to implement order, but if it's just a number, you could use IntegerField as I showed above.
P.S. Note I wrapped all association models in quotes (like 'Child' not Child). It allows to break circular dependencies, so I recommend to use this way
I Got this Models in models.py:
class Boats(models.Model):
id = models.BigIntegerField(primary_key=True)
created = models.DateTimeField(default=timezone.now, blank=True)
name = models.CharField(max_length=30)
class Meta:
managed = True
db_table = 'boats'
ordering = ['name']
class Clients(models.Model):
id = models.BigIntegerField(primary_key=True)
created = models.DateTimeField(default=timezone.now, blank=True)
code = models.CharField(max_length=100)
class Meta:
managed = True
db_table = 'clients'
ordering = ['name']
==========================
In the views.py; my function do it like that:
f = NewBoatForm(request.POST)
if f.is_valid():
nBoat = f.save()
print 'ID:'+str(nBoat.id)
cBoat = ClientsBoats()
cBoat.client = client
cBoat.boat = nBoat
cBoat.save()
But django fails with this error:
ValueError: save() prohibited to prevent data loss due to unsaved related object 'boat'.
I print the ID but it's Null. Can someOne help me.
You've overridden the primary key field, id, to be something other than an AutoField. Don't do that.
Normally you wouldn't define it at all, and let Django do it automatically. If you are really really sure that a standard integer field is not big enough, you can use BigAutoField. But I doubt you will have more than 2147483647 boats in your database.
I have a Recipe model that holds an M2M field of an IngredientType object. This field, aka ingredient_list, uses the notorious 'through' model via an Ingredient object that adds extra data to my IngredientType.
these are my classes:
class Recipe(models.Model):
user_profile = models.ForeignKey(UserProfile, null=True, blank = True)
name = models.CharField(max_length=200)
photo1 = models.ImageField( upload_to = 'images/recipies', help_text="This photo will show by default")
ingredient_list = models.ManyToManyField(IngredientType,through='Ingredient')
class Ingredient(models.Model):
ingredient_type = models.ForeignKey(IngredientType)
recipe = models.ForeignKey(Recipe)
amount = models.IntegerField()
units = models.CharField(max_length=4,choices=UNIT,
default=None, null=True, blank = True)
class IngredientType(models.Model):
name = models.CharField(max_length=200)
plural_name = models.CharField(max_length=200)
photo = models.ImageField( upload_to = 'images/ingredients')
is_main = models.BooleanField()
i've tried serializing them using the rest_framework:
class IngredientTypeSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = IngredientType
fields=('name', 'plural_name', 'photo', 'is_main')
class IngredientSerializer(serializers.HyperlinkedModelSerializer):
ingredient_type = IngredientTypeSerializer(source = 'ingredient_type')
amount = serializers.Field(source='ingredient_type.amount')
units = serializers.Field(source='ingredient_type.units')
recipe = serializers.Field(source='Recipe.name')
class Meta:
model = Ingredient
fields=('amount', 'units')
class RecipeSerializer(serializers.ModelSerializer):
ingredient_list = IngredientSerializer(source='ingredient_list', many=True, read_only = True)
class Meta:
model = Recipe
fields = ('user_profile', 'name','photo1','ingredient_list')
but when trying to run this, I get an AttributeError :
'IngredientType' object has no attribute 'ingredient_type'
clearly, when I change the line:
ingredient_list = IngredientSerializer(source='ingredient_list', many=True, read_only = True)
to:
ingredient_list = IngredientTypeSerializer(source='ingredient_list', many=True, read_only = True)
that is, change the Serializer, it works, but without fetching me the Ingredient data.
i've used this link:
Include intermediary (through model) in responses in Django Rest Framework
as reference, but obviously it hasn't solved my problems.
Any help would be appreciated.
tnx,
Nitzan
On your Recipe model, for the ingredient_list field you have a ManyToManyField pointing to IngredientType.
On your RecipeSerializer the ingredient_list field is not using the IngredientTypeSerializer but rather the IngredientSerializer.
This is the error. (And it explains the error message — the actual model at the source doesn't have the attribute being looked for by the serializer.)
Beyond this, your naming scheme is massively confusing. "Recipe" is fine, but what you're calling "IngredientType" should probably just be "Ingredient" and then you should find a distinct name for the through table. (Maybe "RecipeIngredientDetails")
I hope that helps.
I have models with nested many-to-many relationships
IndependentTest have many-to-many relationship with Protocols
and Protocols have many-to-many relationship with Format.
So I need to save test results for a particular User against IndependentTests.
EDIT: Here are the models
class IndependentTest(models.Model):
name = models.CharField(max_length=1000)
choice = models.ManyToManyField(to=TestChoice, null=True, blank=True)
required = models.BooleanField()
testType = models.CharField(max_length=200,
choices=Values.CUSTOM_ATTRIB_TYPES)
class TestProtocol(models.Model):
name = models.CharField(max_length=200)
description = models.CharField(max_length=1000)
independent_test = models.ManyToManyField(IndependentTest,
through='PIOrder')
class PIOrder(models.Model):
protocol = models.ForeignKey(TestProtocol)
independent = models.ForeignKey(IndependentTest)
order = models.IntegerField()
class TestFormat(models.Model):
name = models.CharField(max_length = 100)
test_protocol = models.ManyToManyField(TestProtocol, through='FPOrder')
class FPOrder(models.Model):
format = models.ForeignKey(TestFormat)
protocol = models.ForeignKey(TestProtocol)
order = models.IntegerField()
EDIT: I don't know if this is a good approach, but I can make a new class
class CandidateTest (models.Model):
test_value = models.CharField(max_length = 200)
candidate = models.ForeignKey(Candidate)
test = models.ForeignKey(IndependentTest)
testProtocol = models.ForeignKey(TestProtocol)
testFormat = models.ForeignKey(TestFormat)
Although the results will be saved in test_value but what about any other data like the date or place the test was taken?