I have several models that have a user as a foreign key. For example, I have model Profile, model profilePic and model userQuestions - in all that models user is foreign key.
Is the there a way a can get a profile, a profilePic and userQuestions that corresponds to given user in one single json response ?
my models.py is following
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
gender = models.CharField(max_length=2)
name = models.CharField(max_length=200)
birthday = models.DateField(auto_now=False, auto_now_add=False)
weight = models.IntegerField(default=0)
heigth = models.IntegerField(default=0)
sign = models.CharField(max_length=200, choices=SIGNS_CHOICES, default='E')
orientation = models.CharField(max_length=200, choices=ORIENTATION_CHOICES, default='E')
bodytype = models.CharField(max_length=200, choices=BODYTYPE_CHOICES, default='E')
education = models.CharField(max_length=200, choices=EDUCATION_CHOICES, default='E')
religion = models.CharField(max_length=200, choices=RELIGION_CHOICES, default='E')
smoking = models.CharField(max_length=200, choices=SMOKING_CHOICES, default='E')
alcohol = models.CharField(max_length=200, choices=ALCOHOL_CHOICES, default='E')
kids = models.CharField(max_length=200, choices=KIDS_CHOICES, default='E')
pets = models.CharField(max_length=200, choices=KIDS_CHOICES, default='E')
location = models.CharField(max_length=100)
latitude = models.FloatField()
longtitude = models.FloatField()
class ProfileFields(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
text = models.TextField()
order = models.IntegerField(default=0)
class ProfilePic(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
profilePic = models.ImageField(upload_to='Images/', default='Images/None/No-img.jpg')
class Pics(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
pic = models.ImageField(upload_to='Images/', default='Images/None/No-img.jpg')
UPD
tried to add
def to_representation(self, data):
profile_info = {kv: data[kv] for kv in data if kv in list(ProfileSerializer.Meta.fields)}
profile_pic_info = {kv: data[kv] for kv in data if kv in list(ProfilePicSerializer.Meta.fields)}
return super(DeviceInfoSerializer, self).to_representation({
'profile': profile_info,
'profile_pic': profile_pic_info,
})
but now it says object 'User' is not iteratable
You can add them as nested serializers to your UserSerializer. Something like this.
class ProfileSerializer(serializers.ModelSerializer)
class Meta:
model = Profile
fields = '__all__'
class ProfilePicSerializer(serializers.ModelSerializer)
class Meta:
model = ProfilePic
fields = '__all__'
class UserQuestionsSerializer(serializers.ModelSerializer)
class Meta:
model = UserQuestions
fields = '__all__'
class UserSerializer(serializers.ModelSerializer)
profile = ProfileSerializer(many=False)
profilepic = ProfilePicSerializer(many=False)
user_questions = UserQuestionsSerializer(mane=True)
class Meta:
model = User
fields = '__all__'
I struggled with the same issue about a month ago and found no good answer on SO.
Sardorbek's answer is totally correct in case you dont care if json response is flat or not. JSON created by that code is something like:
{
"profile": {"name": ..., "gender": ..., "birthday": ...,},
"profile_pic": {"profilePic": ...., },
"user_questions": [{...}, {...}, ...]
User model attributes go here...
}
but if you want it to be flat, like:
{
"name": ...,
"gender": ...,
"birthday": ...,
....
"profilePic": ...,
"user_questions": [{...}, {...}, ...],
User model attributes go here...
}
You need a little hack here. Override to_internal_value in UserSerializer class in this way:
def to_internal_value(self, data):
profile_info = {kv: data[kv] for kv in data if kv in list(ProfileSerializer.Meta.fields)}
profile_pic_info = {kv: data[kv] for kv in data if kv in list(ProfilePicSerializer.Meta.fields)}
return super(DeviceInfoSerializer, self).to_internal_value({
'profile': profile_info,
'profile_pic': profile_pic_info,
})
You may need to override to_representation too, with similar idea.
Related
I have the following models:
class Country(models.Model):
"""
Country model
"""
# With name and isoCode (charfield)
...
class State(models.Model):
"""
State model
"""
# With name and isoCode (charfield)
country = models.ForeignKey(Country, on_delete=models.CASCADE)
...
class City(models.Model):
"""
City model
"""
name = models.CharField(max_length=32)
state = models.ForeignKey(State, on_delete=models.CASCADE)
country = models.ForeignKey(Country, on_delete=models.CASCADE)
...
And UserLocation referenced by:
class Location(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="location")
country = models.ForeignKey(Country, on_delete=models.CASCADE)
state = models.ForeignKey(State, on_delete=models.CASCADE)
city = models.ForeignKey(City, on_delete=models.CASCADE)
How do I build a serializer that creates UserLocation, as well as return the info in JSON?
I have tried
class LocationSerializer(serializers.ModelSerializer):
country = serializers.SlugRelatedField(slug_field="isoCode", queryset=Country.objects.all())
state = serializers.SlugRelatedField(slug_field="isoCode", queryset=State.objects.filter(country__isoCode=country))
city = serializers.SlugRelatedField(slug_field="name", queryset=City.objects.all())
class Meta:
model = Location
fields = ["user", "country", "state", "city"]
But it does not work, it gives the error
{"state":["Object with isoCode=BC does not exist."],...
How does one create a dynamic linked serializer? Or how does one work around this?
I think you want following serializer
class LocationSerializer(serializers.ModelSerializer):
country = serializers.ReadOnlyField(source="country.isocode")
state = serializers.ReadOnlyField(source="state.isocode")
city = serializers.ReadOnlyField(source="city.name")
class Meta:
model = Location
fields = ["user", "country", "state", "city"]
However, it seems your model design is incorrect.
You don't need to have country, state foreign keys in Location model, since City model has state, State model has country.
For future users. I found that
class LocationSerializer(serializers.ModelSerializer):
'''Used to serialize user location'''
country = serializers.SlugRelatedField(slug_field="isoCode", queryset=Country.objects.all(), required=True)
class Meta:
model = Location
fields = ["user", "country"]
extra_kwargs = {"user": { "validators":[UniqueValidator(queryset=Location.objects.all(), message=_("User already has a location. Please update user location. Hint: use PUT request"))]}}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if "country" in kwargs['context']['request'].data and len(Country.objects.get(isoCode=self.initial_data["country"]).state_set.all()) > 0:
self.fields["state"] = serializers.SlugRelatedField(slug_field="isoCode", queryset=State.objects.filter(country__isoCode=self.initial_data["country"]), required=True)
if "state" in kwargs['context']['request'].data and len(State.objects.get(isoCode=self.initial_data["state"], country__isoCode=self.initial_data["country"]).city_set.all()) > 0:
self.fields["city"] = serializers.SlugRelatedField(slug_field="name", queryset=City.objects.filter(state__isoCode=self.initial_data["state"],country__isoCode=self.initial_data["country"]), required=True)
Seemed to work for my case
I want to post a movie into the collection's movie field( list of movies).
I define the model as
class Movie(models.Model):
# collection = models.ForeignKey(Collection, on_delete = models.CASCADE) #, related_name='reviews'
title = models.CharField(max_length=200)
description = models.CharField(max_length=200)
genres = models.CharField(max_length=200)
uuid = models.CharField(max_length=200)
def __str__(self):
return self.title
class Collection(models.Model):
title = models.CharField(max_length=200)
uuid = models.CharField(max_length=200, primary_key = True)
description = models.CharField(max_length=200)
movie = models.ForeignKey(Movie, on_delete = models.CASCADE)
def __str__(self):
return self.title
this is how i am using the viewset
class CollectionViewSet(viewsets.ModelViewSet):
queryset = models.Collection.objects.all()
serializer_class = serializers.CollectionSerializer
but i am not able to enter values for the movie field
enter image description here
also my serializer
class CollectionSerializer(serializers.ModelSerializer):
class Meta:
model = Collection
fields = '__all__'
By default, DRF will represent the relationship with a PrimaryKeyRelatedField, thus expecting a movie ID.
To achieve what you want (create an instance of movie with a collection), you need to overwrite the foreign key field in your serializer with your own Movie serializer.
class MovieSerializer(serializers.ModelSerializer):
class Meta:
model = Movie
fields = '__all__'
class CollectionSerializer(serializers.ModelSerializer):
movie = MovieSerializer()
class Meta:
model = Collection
fields = '__all__'
def create(self, validated_data):
movie = validated_data.pop('movie')
movie = Movie .objects.create(**movie )
collection = Collection.objects.create(movie=movie, **validated_data)
return collection
You need to overwrite the create method so when creating a Collection, you also create a movie.
However, I am not sure the foreign key is set in the right model in your model. (a movie belongs to many collection but not the other way around?) If that's not what you want, just reverse the logic for the serializer.
Edit:
Sending the following should work fine:
{ "uuid": "1001",
"title": "Action",
"description": "Action Movies",
"movie": { "title": "The Burkittsville 7",
"description": "The story of Rustin Parr.",
"genres": "Horror",
"uuid": "5e904"
}
}
The only problem as I mentionned earlier is in your model you defined the foreign key field in collection. So it expects one single movie instance and not a list, thus I took off the brackets you put around movie. Maybe you should consider setting the foreign key in the Movie model, or use a Many to many relationship.
#models.py
class Movie(models.Model):
# collection = models.ForeignKey(Collection, on_delete = models.CASCADE) #, related_name='reviews'
title = models.CharField(max_length=200)
description = models.CharField(max_length=200)
genres = models.CharField(max_length=200)
uuid = models.CharField(max_length=200)
def __str__(self):
return self.title
class Collection(models.Model):
title = models.CharField(max_length=200)
uuid = models.CharField(max_length=200, primary_key = True)
description = models.CharField(max_length=200)
movie = models.ManyToManyField(Movie, on_delete = models.CASCADE)
def __str__(self):
return self.title
serializers.py:
class MovieSerializer(serializers.ModelSerializer):
class Meta:
model = Movie
fields = '__all__'
class CollectionSerializer(serializers.ModelSerializer):
movie = MovieSerializer(read_only=True, many=True)
class Meta:
model = Collection
fields = '__all__'
hope this will give you better unserstand this will work for you
Order table
class Orders(models.Model):
restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE, blank=True, null=True)
tableid=models.IntegerField()
orderid=models.IntegerField()
total_amount = models.DecimalField(max_digits=10, decimal_places=2)
Articles table to save articles like pizza
class OrderArticle(models.Model):
order = models.ForeignKey(Orders, on_delete=models.CASCADE)
article = models.ForeignKey(Articles, on_delete=models.CASCADE)
# article_options = models.ManyToManyField(ArticlesOptions)
Article options to save extra topping or any option available
class OrderArticleOptions(models.Model):
# order = models.ForeignKey(Orders, on_delete=models.CASCADE)
article_option = models.ForeignKey(ArticlesOptions, on_delete=models.CASCADE)
order_article = models.ForeignKey(OrderArticle, on_delete=models.CASCADE)
quantity = models.IntegerField(default=1)
price = models.DecimalField(max_digits=10, decimal_places=2)
EDIT
Article Option table
class ArticlesOptions(models.Model):
articleoptionrestaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE , blank=True, null=True)
optionname = models.ForeignKey(ArticlesOptionsName, on_delete=models.CASCADE, related_name="optionnames")
min = models.IntegerField()
max = models.IntegerField()
choice_price = models.DecimalField(max_digits=10, decimal_places=2)
choice = models.CharField(max_length=255)
def __str__(self):
return str(self.optionname)
So Now issue is When I try to get all data in one serialize I am not able to get . I am using this example to get
https://www.django-rest-framework.org/api-guide/relations/
EDIT
My serilizers are
class OrderSerializer(serializers.ModelSerializer):
restaurant=RestaurantSerializer(required=False)
class Meta:
model = Orders
fields = ['restaurant','tableid', 'orderid', 'total_amount']
class ArticlesSerializer(serializers.ModelSerializer):
order = OrderSerializer(read_only=True)
article=ListArticleSerializer(read_only=True)
class Meta:
model = OrderArticle
fields = ['order', 'article']
class ArticlesOptionSerializer(serializers.ModelSerializer):
article_option = ListCategoriesSerializer( read_only=True)
order_article=ArticlesSerializer(read_only=True)
class Meta:
model = OrderArticleOptions
fields = ['article_option','order_article','quantity','price']
depth=1
My view.py is
class OrderedArticles(APIView):
def get(self, request, restid):
Options=OrderArticleOptions.objects.filter(order_article=1)
orderserlizer=ArticlesOptionSerializer(Options , many=True)
return Response(success_response({'orders': orderserlizer.data},
"Restaurant with this all data."), status=status.HTTP_200_OK)
My JSON Response is
"article_option":{ },
"order_article":{
"order":{
"restaurant":{ },
"tableid":12,
"orderid":1,
"total_amount":"0.00"
},
"article":{
"id":1,
"category":{ },
"ingredient":[ ],
"articleoptionnames":{ },
"restaurant":{ },
"articlename":"Article1",
"price":"1.90",
"pickuptax":6,
"dineintax":21,
"description":"This is a tekst field with more information about the product",
"image":"/media/Article/c1.264f3b28_sxcPiqi.png"
}
},
While I want these "article_option" to be as child of article like Article {article_option1, article_option2} but its creating new objects with every new article option.
If I understand you correctly, you want to return a representation of OrderArticle which have ArticleOption objects as its children. Which means you should instantiate an ArticlesSerializer in your view, but also modify ArticlesSerializer so that it includes all related article_options as a list (using the source attribute). Something like the following:
class ArticlesOptionSerializer(serializers.ModelSerializer):
article_option = ListCategoriesSerializer(read_only=True)
class Meta:
model = OrderArticleOptions
fields = ['article_option', 'order_article', 'quantity', 'price']
class ArticlesSerializer(serializers.ModelSerializer):
order = OrderSerializer(read_only=True)
article = ListArticleSerializer(read_only=True)
article_options = ArticlesOptionSerializer(read_only=True, source='orderarticleoptions_set', many=True)
class Meta:
model = OrderArticle
fields = ['order', 'article', 'article_options']
Then in your view, you should instantiate your ArticlesSerializer with the appropriate OrderArticle object:
class OrderedArticles(APIView):
def get(self, request, restid):
order_article = OrderArticle.objects.get(pk=1) # get pk/id from request
serializer = ArticlesSerializer(order_article)
return Response(serializer.data, status=status.HTTP_200_OK)
My serializers are not showing related Models. I mean the models that have got many-to-one relationship. Please see the code below. Forgive my English.
Completely confusing me please help. I am new to django. I am trying to save my Draft js ContentState to the database. I have made the Post model a Foreignkey to my Block models. But When I try to retrieve the data using django-rest-framework serializers the blocks are not displaying.
Same applies with the Blocks I tried to serialize them on their own but the inlineStyleRanges and entityRanges data is not coming up.
#models.py
class Post(models.Model):
created_by= models.ForeignKey(User, on_delete=models.CASCADE)
cat= models.ForeignKey(Category, on_delete=models.CASCADE)
class Block(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE)
key = models.CharField(max_length=255)
text = models.TextField()
type = models.CharField(max_length=90)
depth = models.IntegerField(default=0)
class InlineStyleRange(models.Model):
block = models.ForeignKey(Block, on_delete=models.CASCADE)
offset = models.IntegerField()
length = models.IntegerField()
style = models.CharField(max_length=90)
class EntityRange(models.Model):
block = models.ForeignKey(Block, on_delete=models.CASCADE)
offset = models.IntegerField()
length = models.IntegerField()
key = models.IntegerField()
class Data(models.Model):
data = models.TextField()
class EntityRangeData(Data):
enityrange = models.ForeignKey(EntityRange, on_delete=models.CASCADE)
#Entity map here we go
class EntityEntry(models.Model):
key = models.IntegerField()
block= models.ForeignKey(Block, on_delete=models.CASCADE)
type = models.CharField(max_length=90)
mutability = models.CharField(max_length=90)
class EntityEntryData(Data):
entityentry = models.ForeignKey(EntityEntry, on_delete=models.CASCADE)
```#Serializers.py```
class EntityEntryDataSerializer(serializers.ModelSerializer):
class Meta:
model = models.EntityEntryData
fields = "__all__"
class EntityEntrySerializer(serializers.ModelSerializer):
data = EntityEntryDataSerializer()
class Meta:
model = models.EntityEntry
fields = "__all__"
class EntityRangeDataSerializer(serializers.ModelSerializer):
class Meta:
model = models.EntityRangeData
fields = "__all__"
class EntityRangeSerializer(serializers.ModelSerializer):
data = EntityRangeDataSerializer()
class Meta:
model = models.EntityRange
fields = "__all__"
class InlineStyleRangeSerializer(serializers.ModelSerializer):
class Meta:
model = models.InlineStyleRange
fields = "__all__"
class BlockSerializer(serializers.ModelSerializer):
inlineStyleRanges = InlineStyleRangeSerializer(many=True, required=False)
entityRanges = EntityRangeSerializer(many=True, required=False)
class Meta:
model = models.Block
fields = "__all__"
class PostSerializer(serializers.ModelSerializer):
blocks = BlockSerializer(many=True, required=False, read_only=True)
class Meta:
model = models.Post
fields = "__all__"
The output be like
[
{
"id": 1,
"created_by": 1,
"cat": 2
}
]
The Block model does not have attributes called inlineStyleRanges or entityRanges, so you should either use related_name in the ForeignKey field or you could specify a source argument in the serializer.
inlineStyleRanges = InlineStyleRangeSerializer(many=True, required=False, source='inlinestyle_set')
entityRanges = EntityRangeSerializer(many=True, required=False, source='entityrange_set')
You might have to include both nested serializers in BlockSerializer.Meta.fields as well. Instead of "__all__", use a list:
fields = ['id', 'post', 'key', 'inlineStyleRanges', 'entityRanges']
I have the following models
class STUser(AbstractBaseUser):
email = models.EmailField(unique=True)
name = models.CharField(max_length=255)
companyname = models.CharField(max_length=200, blank=True, null=True)
...
class VenuePermissions(models.Model):
user = models.ForeignKey(STUser, on_delete=models.CASCADE)
venue = models.ForeignKey(Venue, on_delete=models.CASCADE)
signupvaildatestring = models.CharField(max_length=200, blank=True, null=True)
...
I want to grab all the STUser objects and grab all their permissions.
So what I would like is to grab all the VenuePermissions objects. And grab the user and venue object of each venuePermission
Two ways I can do this. use the VenuePermissions_set attribute on STUser but then how do I grab the venue when its just going to be a pk value?
Or focus on the VenuePermissions objects and grab the user and venue from the pk values but how?
I remember nested queries, and I kinda did one in my browse code.
here is an example:
rooms = Room.objects.filter(venue=OuterRef('pk'), sixtyroundseatingoption= True)
venuelist = venuelist.annotate(sixtyrounds=Exists(rooms))
venuelist = venuelist.filter(Q(sixtyrounds = True) | Q(fullbuyoutsixtyroundseatingoption = True))
I've done the set objects in a serializer before
Example serializer:
class RoomAndImageSerializer(serializers.ModelSerializer):
roomimage_set = RoomImageSerializer(many=True, read_only=True)
class Meta:
model = Room
fields = ('pk','name')
any help with this query would be appreciated!
So this is what I am currently trying, I will post an answer if this works:
class VenueUserList(ListAPIView):
serializer_class = VenueUserListSerializer
queryset = VenuePermissions.objects.select_related('user').select_related('venue').filter(signupvaildatestring=None)
class VenueUserListSerializer(serializers.ModelSerializer):
user = UserSerializer()
venue = VenueSerializer()
class Meta:
model = VenuePermissions
fields = ('user', 'venue', 'isvenueviewer', 'isvenueeventplanner', 'isvenueadministrator')
Here is the answer. However I still need to group venues by user. Working on that.
class VenueUserList(ListAPIView):
serializer_class = VenueUserListSerializer
queryset = VenuePermissions.objects.select_related('user').select_related('venue').filter(signupvaildatestring=None)
class VenueUserListSerializer(serializers.ModelSerializer):
user = UserSerializer()
venue = VenueSerializer()
class Meta:
model = VenuePermissions
fields = ('user', 'venue', 'isvenueviewer', 'isvenueeventplanner', 'isvenueadministrator')