Django Nested Serialization - django

this is my serializor.py
class AddressSerializor(serializers.ModelSerializer):
class Meta:
model = Address
fields = '__all__'
class PersonSerializer(serializers.ModelSerializer):
a = AddressSerializor(many=True)
class Meta:
model = Persion
fields = ('id' ,'name', 'age', 'a' )
this is my models page :
class Persion(models.Model):
name = models.TextField(max_length=200)
class Address(models.Model):
city = models.TextField(max_length=300)
state = models.TextField(max_length=301)
cuntry = models.TextField(max_length=222)
I am getting this error:
Got AttributeError when attempting to get a value for field `a` on serializer `PersonSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `Persion` instance.
Original exception text was: 'Persion' object has no attribute 'a'.
can any one help me what am i missing, as iam new to django i may miss the basic syntax too, Iam feguring this nearly 2 days no luck

Since according to your comments you have one address per person, you can technically put the address fields in the Person model, but if you want to separate it you need to link them like
class Address(models.Model):
city = models.TextField(max_length=300)
state = models.TextField(max_length=301)
cuntry = models.TextField(max_length=222)
class Persion(models.Model):
name = models.TextField(max_length=200)
address = models.OneToOneField(Address,
on_delete=models.CASCADE,
null=True, # allow field to be null
blank=True) # allow field to be blank (for admin interface)
This way the Persion would have an address field that would be of type Address model.
and your serializers
class AddressSerializor(serializers.ModelSerializer):
class Meta:
model = Address
fields = '__all__'
class PersonSerializer(serializers.ModelSerializer):
address = AddressSerializor() #note that address is the same name in the Persion model
class Meta:
model = Persion
fields = ('id' ,'name', 'address' )
in your question you had age as a field but you don't have an age field in the Persion model, you will need to add that.
check out the fields in Django docs
https://docs.djangoproject.com/en/3.2/ref/models/fields

You need to create a foreign key relationship between a Person and an Address. You can add the foreign key declaration to your models, and then generate/run migrations using python manage.py makemigrations <name-of-app> and python manage.py migrate.
class Person(models.Model):
... # the other model fields
address = models.ForeignKey(
"Address",
on_delete=models.SET_NULL,
null=True,
blank=True,
)

Related

Serialize many-to-many relation with intermediate model in Django Rest

I tried to check another topics, but didn't found a solution...
I have a many-to-many model, that have intermediate model with another field additional_field inside.
class BoardField(models.Model):
title = models.CharField(max_length=500, default='')
class Article(models.Model):
title = models.CharField(max_length=500, default='')
fields = models.ManyToManyField(BoardField, through='ArticleField', through_fields=('article', 'board_field'))
class ArticleField(models.Model):
article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='task')
board_field = models.ForeignKey(BoardField, on_delete=models.CASCADE)
additional_field = models.CharField(max_length=200, blank=True, null=True)
I want serialize Article with structure:
[
"title":"Title",
"fields":[
{
"board_field": {
"title":"Title"
},
"additional_field":"Additional info"
}
]
]
So, I wrote serializer:
class BoardFieldSrl(serializers.ModelSerializer):
class Meta:
model = BoardField
fields = (
'title',
)
class ArticleFieldSrl(serializers.ModelSerializer):
board_field = BoardFieldSrl()
class Meta:
model = ArticleField
fields = (
'board_field',
'additional_field',
)
class ArticleListSrl(serializers.ModelSerializer):
fields = ArticleFieldSrl(many=True)
class Meta:
model = Article
fields = (
'title',
'fields',
)
But I always got an error:
Got AttributeError when attempting to get a value for field `board_field` on serializer `ArticleFieldSrl`.
The serializer field might be named incorrectly and not match any attribute or key on the `BoardField` instance.
Original exception text was: 'BoardField' object has no attribute 'board_field'.
I made another several examples, but they doesn't gave my result, that I need... My maximum - I got BoardField with levels, but without intermediate model...
Can you help me with serializer, that return structure, that I mentioned above? It must include intermediate model ArticleField and nested BoardField.
Try fields = ArticleFieldSrl(source='articlefield_set', many=True)
You didn't specified a related_name at M2M field so the default naming is applied which is 'Intermediate model name'_set and if you want to use the fields on M2M relation you have to tell the serializer where to look for.
EDIT:
Camel removed from articlefield_set, model name is always converted to lower case

Django RestFramework - parent-child model serializer with DB views?

I am trying to implement a serializer that returns a parent record with its children embedded in the response json object.
My model for the parent and child are both based on database views:
class ProductContributorView(models.Model): # its a model of a view
id = models.IntegerField(primary_key=True)
product_id = models.ForeignKey('ProductTitleView', on_delete=models.DO_NOTHING, related_name='contributors')
sequenceNumber = models.IntegerField()
name = models.CharField(max_length=180)
role = models.CharField(max_length=8, null=True)
description = models.CharField(max_length=1408)
class Meta:
managed = False
ordering = ['sequenceNumber',]
class ProductTitleView(models.Model):
id = models.IntegerField(primary_key=True)
isbn = models.CharField(max_length=80)
titleText = models.CharField(max_length=300)
class Meta:
managed = False
ordering = ['titleText', 'isbn',]
Here are the serializers:
class ProductContributorViewSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = ProductContributorView
fields = ('id', 'product_id', 'sequenceNumber', 'name', 'role', 'description')
def create(self, validated_data):
contributor = ProductContributorView.objects.create(
id=validated_data['id'],
product_id=validated_data['product_id'],
sequenceNumber=validated_data['sequenceNumber'],
name=validated_data['name'],
role=validated_data['role'],
description=validated_data['description'])
return contributor
class ProductTitleViewSerializer(serializers.HyperlinkedModelSerializer):
contributors = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = ProductTitleView
fields = ('id', 'isbn', 'titleText', 'contributors')
Here are the views:
class ProductTitleViewList(generics.ListAPIView):
queryset = ProductTitleView.objects.all()
serializer_class = ProductTitleViewSerializer
class ProductContributorViewList(generics.ListAPIView):
queryset = ProductContributorView.objects.all()
serializer_class = ProductContributorViewSerializer
The basic idea is to have the contributors - author, illustrator, etc - returned with the book title based on the FK in the ProductContributorView view matching the id in the ProductTitleView.
When I run this, however, I get the following error:
1054, "Unknown column 'jester_productcontributorview.product_id_id' in 'field list'"
I didn't specify product_id_id in the field list, and I've also tried referring to the field as just product in the field list, but it still repeats the _id_id suffix. Hoping someone will point me to documentation where the FK naming conventions are explained or tell me what to change in the field list. Thanks!
You may just want to try renaming that product_id ForeignKey to just product.
This hints to why it may be broken, I suspect it's breaking somewhere in the serializers inspection of your models regarding the naming of the product_id field on the model.
When you define a ForeignKey on a model there are two properties available for that field. One is the property you define, the ForeignKey object, and you should use this to get the related model. Behind the scenes Django also creates another property which appends _id to the the foreign key's name, this property represents the IntegerField on the database which stores the relation. If you were to view the table in psql you will see the _id columns (and in your case, _id_id).

nested serializer showing null data

nested serializer showing null data
from rest_framework import serializers
from .models import PlayerTable, ChildTable
class ChildTableSerializer(serializers.ModelSerializer):
# x= ChildTable.objects.all().values
class Meta:
model = ChildTable
fields = ('season','goals','fk')
# fields =('fk',)
class PlayerTableSerializer(serializers.ModelSerializer):
player_details = ChildTableSerializer(many=True, read_only=True)
class Meta:
model = PlayerTable
fields = ('player_details',)
please help data getting by serializer is null
what is the field 'player-details'? It's not a field on your PlayerTable model. You need to use the name of the related field. In your case since you have a ForeignKey relationship ChildTable --> PlayerTable and you haven't specified the related_name, it's childtable_set. So if you do this it should work:
class PlayerTableSerializer(serializers.ModelSerializer):
childtable_set = ChildTableSerializer(many=True, read_only=True)
class Meta:
model = PlayerTable
fields = ('childtable_set',)
Alternatively, change your models naming to be more aligned with Django conventions:
class PlayerDetail(models.Model):
player = models.ForeignKey(Player, db_column="fk", related_name="player_details", null=True, blank=True, on_delete=models.CASCADE)
...
class Meta:
managed = False
db_table = "child_table"
class Player(models.Model):
name = models.CharField(db_column="player_name", ...)
class Meta:
db_table = "player_table"
then your serializer would work because the relation is player_details. This also has the advantage that when you do details.player you get the player object (now, you have to do details.fk but that actually doesn't return the foreign key value, it returns the Player object). Also your models have more pythonic names (Player not PlayerTable). Your code will be much more readable.

Show all field in OneToOneField field type

In Django 1.8.3, I have following classes and they are showing in Admin Panel:
class Address(models.Model):
address_line1 = models.CharField(max_length=64)
address_line2 = models.CharField(max_length=64)
address_line3 = models.CharField(max_length=64)
post_code = models.CharField(max_length=5)
class Customer(models.Model):
name = models.CharField(max_length=64)
address = models.OneToOneField(Address)
Now in Customer form in Admin Panel, the Address is shown as a dropdown menu. My question is: How can I show all fields of the Address class instead of a single dropdown in Customer form as it is a OneToOneField in Customer class?
Thanks in advance
If I were you, I would change the structure so that Address could be an inline. In the models.py:
class Customer(models.Model):
name = models.CharField(max_length=64)
class Address(models.Model):
costumer = models.OneToOneField(Costumer)
address_line1 = models.CharField(max_length=64)
address_line2 = models.CharField(max_length=64)
address_line3 = models.CharField(max_length=64)
post_code = models.CharField(max_length=5)
And then, in the admin.py:
class AddressInline(admin.StackedInline):
model = Address
extra = 1
max_num = 1
class CostumerAdmin(admin.ModelAdmin):
inlines = [AddressInline]
admin.site.register(Costumer, CostumerAdmin)
for field in YourModelClass._meta.get_fields():
# iterate through main model's fields
if isinstance(field, OneToOneField):
# if the field is an OneToOneField
for field2 in YourModelClass._meta.get_field(field.name).related_model._meta.get_fields():
# iterate through the OneToOneField's fields
fieldname = field2.name
fieldvalue = field2.value_from_object(getattr(instance, field.name))
# getattr simulates instance.`field.name`
else:
fieldname = field.name
fieldvalue = field.value_from_object(instance)
where YourModelClass is the model that contains more OneToOneField objects and/or other basic models. In the example above, it is Address, and instance is the instance of the model.
Please notice you don't need the instance to get the field names, but you need it if you want to get the field value.
I use this code to convert an instance of a model into a context dictionary for dynamic settings, i'm not sure it's the best solution.

how to conditionally save a form using django multi table inheritance

I have the following form:
class PlaceForm(forms.ModelForm):
class Meta:
model = Place
I have the following models:
class Place(models.Model):
customer = models.ForeignKey(Customer)
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
class Restaurant(Place):
serves_hot_dogs = models.BooleanField()
serves_pizza = models.BooleanField()
In my view I want to conditionally save either a Place or a Restaurant depending on the incoming url.
I have tried the following:
if form.is_valid():
place = form.save(commit=False)
place.customer = customer
place.save()
if url_name == 'restaurant':
restaurant = Restaurant(place_ptr_id=place.id)
restaurant.save()
This creates a place from the form and then tries to create a restaurant, but fails with following: (1048, "Column 'customer_id' cannot be null")
This is telling me that a new row for a new place is trying to be inserted and then the restaurant row.
I see a few different options:
Convert the Place to a restaurant and save the additional to the converted object.
Conditionally change the model type of the form to either Place or Restaurant
How can I accomplish saving the different parent and child objects conditionally?
It is related to Django model inheritance: create sub-instance of existing instance (downcast)? which suggests how to add object with existing base class object.
You may want to look at my question: Derived model filefield not available
In nutshell what you have to do is
restaurant = Restaurant(place_ptr_id=place.id)
restaurant.__dict__.update(place.__dict__)
restaurant.save()
You can add null=True and blank=True.
models:
class Place(models.Model):
customer = models.ForeignKey(Customer, null=True, blank=True)
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)