I am fairly new to django rest framework. I have these tables in my database:
1) MainCategroies - which stores a list of all education fields.
2) College - which stores list of all college of my state.
3) CollegeCategoryLink - which stores the link between colleges and the categories to which they belong( here same college can fall under multiple categories). created a model with two foreign-key column
4) Users - the users of my app.
5) UserCategoryLink - link between the users and their selected categories. created a model with two foreign-key column
6) UserCollegeLink - link between the users and their selected colleges. created a model with two foreign-key column
Now the users will select their preferable categories from the list
and that will be stored in my database and then i will return the
related colleges back. All the data will come in json format from my
ionic app.
i have written serializers for each model and created viewsets for CRUD operations. Now i am confused, how to store the data through viewset methods? I am currently doing this:
class UserCategoryLinkViewset(viewsets.ViewSet):
serializer_class = UserCategoryLinkSerializer
def create(self, request):
selectedCats = []
collegeList = []
data = JSONParser().parse(request)
for field in data:
selectedCats.append(field['cat'])
ucl = UserCategoryLink()
ucl.user = collegeAppUser.objects.get(id=field['user'])
ucl.cat = MainCategories.objects.get(id=field['cat'])
if not UserCategoryLink.objects.filter(user=field['user'], cat=field['cat']).exists():
ucl.save()
for cats in selectedCats:
queryset = CollegeCategoryLink.objects.filter(category_id=cats)
serializer = CollegeCategoryLinkSerializer(queryset, many=True)
for clg in serializer.data:
queryset_college = College.objects.filter(id=clg['college_id'])
serializer_college = CollegeSerializer(queryset_college, many=True)
collegeList.append(serializer_college.data)
return JSONResponse(collegeList)
And here are my serializers:
from rest_framework import serializers
from manageApp.models import collegeAppUser,MainCategories,UserCategoryLink, CollegeCategoryLink, College, UserCollegeLink
class collegeAppUserSerializer(serializers.ModelSerializer):
class Meta:
model = collegeAppUser
fields = ('id', 'username', 'password')
class MainCategorySerializer(serializers.ModelSerializer):
class Meta:
model = MainCategories
fields = ('id', 'category_name')
class UserCategoryLinkSerializer(serializers.ModelSerializer):
class Meta:
model = UserCategoryLink
fields = ('id', 'user', 'cat')
class CollegeCategoryLinkSerializer(serializers.ModelSerializer):
class Meta:
model = CollegeCategoryLink
fields = ('id', 'college_id', 'category_id')
class CollegeSerializer(serializers.ModelSerializer):
class Meta:
model = College
fields = ('id', 'college_name', 'college_url')
class UserCollegeLinkSerializer(serializers.ModelSerializer):
class Meta:
model = UserCollegeLink
fields = ('id', 'user', 'college')
But this is not the correct way to accomplish what i need to do as i am directly setting the data in my model and saving it, no use of serializer in here. I want to store the data through serializer rather then directly using my model.
Try to relate models.
You can see the rest framework documentation: Relations!
For your case, you would use Nested relationships! =]
Related
Why filter doesn't apply? It returns the all details records for each master.
models
class Master(models.Model):
pass
class Detail(models.Model):
master = models.ForeignKey(Master, models.CASCADE, related_name='details')
some_field = models.BooleanField()
serializers
class DetailListSerializer(serializers.ModelSerializer):
class Meta:
model = Detail
fields = ('some_field',)
class MasterListSerializer(serializers.ModelSerializer):
details = DetailListSerializer(many=True)
class Meta:
model = Master
fields = ('id', 'details')
views
data = Master.objects.filter(details__some_field=True)
serializer = MasterListSerializer(data, many=True)
When you call
data = Master.objects.filter(details__some_field=True)
you're retrieving a queryset with one Master object for each Detail object in the database that has some_field=True. Those individual Master objects that make up the queryset still have all of their child details per standard Django ORM relations, as you can see if you were to do the following:
for master in data:
print(master.details.all())
You merely retrieved Masters you know have at least one Detail child with some_field=True. (And as such, the data queryset could have duplicate Master objects, since a Master may have multiple Detail children with some_field=True.)
For workarounds, one alternative is to add custom logic in your MasterListSerializer that limits which details are shown for a given Master. Something like:
# Serializers
class DetailSerializer(serializers.ModelSerializer):
class Meta:
model = Detail
fields = ('some_field',)
class MasterWithFilteredDetailsSerializer(serializers.ModelSerializer):
details = serializers.SerializerMethodField()
def get_details(self, master):
# Filter to only include the relevant Details with some_field=True
qs = master.details.filter(some_field=True)
serializer = DetailSerializer(instance=qs, many=True)
return serializer.data
class Meta:
model = Master
fields = ('id', 'details')
# Views
# Note that we use `distinct` here to avoid duplicates
masters = Master.objects.filter(details__some_field=True).distinct()
serializer = MasterWithFilteredDetailsSerializer(masters, many=True)
Or perhaps if it works for you, you could do the query and serialization the other way around, starting with Details with some_field=True, like:
# Serializers
class MasterSerializer(serializers.ModelSerializer):
class Meta:
model = Master
fields = ('id',)
class DetailSerializer(serializers.ModelSerializer):
master = MasterSerializer()
class Meta:
model = Detail
fields = ('some_field', `master`,)
# Views
details = Detail.objects.filter(some_field=True).select_related('master')
details_with_masters = DetailSerializer(details, many=True)
Model:
class Demo(models.Model):
name = models.CharField(max_length=255)
desc = models.TextField()
user = models.ForeignKey('auth.User', on_delete=models.CASCADE)
Serializer:
class DemoSerializer(serializers.ModelSerializer):
class Meta:
model = Demo
fields = '__all__'
I have form in frontend side where I'm adding name, desc and assigning to User so here I'm getting on an issue.
I'm passing data to API {name: "demo", desc: "lorem ipsum", user: 1 }
It's working on save but after saving it's return same response but I want user first_name, last_name, and email in return response.
Because I have a table showing a list of demo table content. but always getting only User ID not a detail of user.
If I'm increasing depth of Serializer It's creating an issue in save time but on get records time I'm getting all details of User model. Like Password also in response so that is a security issue for me show all thing.
You can use depth = 1 to get all the data of foreign key object:
class DemoSerializer(serializers.ModelSerializer):
class Meta:
model = Demo
fields = '__all__'
depth = 1
You could separate the Create and Retrieve serializer. For example, the create serializer will be the one you are currently using:
class DemoCreateSerializer(serializers.ModelSerializer):
class Meta:
model = Demo
fields = '__all__'
On the other hand, Retrieve serializer will serialize the User with a different serializer using Nested Relationship.
class DemoRetrieveSerializer(serializers.ModelSerializer):
user = UserMinimalSerializer # or you could use your UserSerializer, see the last part of the answer
class Meta:
model = Demo
fields = ('id', 'name', 'desc', 'user')
read_only = ('id', 'name', 'desc', 'user',)
In your view, you will create the data with the first serializer and respond with the second. An example using APIView:
class DemoView(APIView):
def post(self, request, format=None):
create_serializer = DemoCreateSerializer(data=request.data)
if create_serializer.is_valid():
instance = create_serializer.save()
retrive_serializer = DemoRetrieveSerializer(instance)
return Response(retrive_serializer.data, status=status.HTTP_201_CREATED)
return Response(create_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
You might have to customize DRF provided views to achieve this, i.e. for Generic views.
Since you don't want to include all the fields of User model, you will have to write a minimal representation of User using another serializer.
class UserMinimalSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'first_name', 'last_name', 'email')
read_only = ('id', 'first_name', 'last_name', 'email',)
Hope it helps.
In django model serializer, the typical use case is like below
class SalesManSerializer(serializers.ModelSerializer):
class Meta:
module = models.SalesMan
fields = ('id', 'name')
The id, name are all from the salse man model.
But if I have another model called Order which referenced to SalseMan. I want to query how many order each sales man get.
The SQL would be like
SELECT ID, NAME, COUNT(ORDER.ID) FROM SALSE_MAN, ORDER WHERE ORDER.UID = SALSE_MAN.ID GROUP BY SALSE_MAN.ID
How to serializer this query?
Model serializers serialize only model fields by default. But good news is that you always can add custom fields to your serializer.
Don't know what your models exactly are, but in code it may look like:
class SalesManSerializer(serializers.ModelSerializer):
total_orders = serializers.IntegerField()
class Meta:
module = models.SalesMan
fields = ('id', 'name', 'total_orders')
Serializers are responsible only for serializing. They don't construct querysets for us. That said you need to annotate query set yourself. Either in a view or wherever the queryset will be initialized and passed to the serializer you can use annotation to annotate object with count of orders.
For ex:
class SalesManViewSet(viewsets.ModelViewSet):
serializer_class = SalesManSerializer
def get_queryset(self):
return SalesMan.objects.annotate(
total_trucks = Count('orders'),
)
you can add it using a methodfield
class SalesManSerializer(serializers.ModelSerializer):
count = serializers.SerializerMethodField()
class Meta:
model = models.SalesMan
fields = ('id', 'name', 'count')
def get_count(self, instance):
return Order.objects.count()
I have a Cart model and a CartItem model. The CartItem model has a ForeignKey to the Cart model.
Using Django Rest Framework I have a view where the API user can display the Cart, and obviously then I want to include the CartItem in the respone.
I set up my Serializer like this:
class CartSerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=True)
cartitem_set = CartItemSerializer(read_only=True)
class Meta:
model = Cart
depth = 1
fields = (
'id',
'user',
'date_created',
'voucher',
'carrier',
'currency',
'cartitem_set',
)
My problem is the second line, cartitem_set = CartItemSerializer(read_only=True).
I get AttributeErrors saying 'RelatedManager' object has no attribute 'product'. ('product' is a field in the CartItem model. If I exclude product from the CartItemSerializer I just get a new AttributeError with the next field and so on. No matter if I only leave 1 or all fields in the Serializer, I will get a error.
My guess is that for some reason Django REST Framework does not support adding Serializers to reverse relationships like this. Am I wrong? How should I do this?
PS
The reason why I want to use the CartItemSerializer() is because I want to have control of what is displayed in the response.
Ahmed Hosny was correct in his answer. It required the many parameter to be set to True to work.
So final version of the CartSerializer looked like this:
class CartSerializer(serializers.ModelSerializer):
cartitem_set = CartItemSerializer(read_only=True, many=True) # many=True is required
class Meta:
model = Cart
depth = 1
fields = (
'id',
'date_created',
'voucher',
'carrier',
'currency',
'cartitem_set',
)
It's important to define a related name in your models, and to use that related name in the serializer relationship:
class Cart(models.Model):
name = models.CharField(max_length=500)
class CartItem(models.Model):
cart = models.ForeignKey(Cart, related_name='cart_items')
items = models.IntegerField()
Then in your serializer definition you use those exact names:
class CartSerializer(serializers.ModelSerializer):
cart_items = CartItemSerializer(read_only=True)
class Meta:
model = Cart
fields = ('name', 'cart_items',)
It would be wise to share your whole code, that is model and serializers classes. However, perhaps this can help debug your error,
My serializer classes
class CartItemSerializer(serializers.ModelSerializer):
class Meta:
model = CartItem
fields = ('id')
class CartSerializer(serializers.ModelSerializer):
#take note of the spelling of the defined var
_cartItems = CartItemSerializer()
class Meta:
model = Cart
fields = ('id','_cartItems')
Now for the Models
class CartItem(models.Model):
_cartItems = models.ForeignKey(Subject, on_delete=models.PROTECT)
#Protect Forbids the deletion of the referenced object. To delete it you will have to delete all objects that reference it manually. SQL equivalent: RESTRICT.
class Meta:
ordering = ('id',)
class Cart(models.Model):
class Meta:
ordering = ('id',)
For a detailed overview of relationships in django-rest-framework, please refer their official documentation
Consider the following serializer
class MyModelSerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = ('id', 'account')
depth = 1
The field account refers to a ForeignKey in MyModel and I want to expose some of the Account fields with this serializer but not all of them.
How do I specify that only account.name and account.email should be serialized?
You can do this by creating your own serializer to use as the nested serializer.
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ('name', 'email', )
You are better off with creating specialized serializers instead of relying on Django REST Framework to create them for you. By default, serializers that are automatically created contain all fields defined on the model.
class MyModelSerializer(serializers.ModelSerializer):
account = AccountSerializer()
class Meta:
model = MyModel
fields = ('id', 'account', )
You can find out more about nested serializers in the Django REST Framework documentation.