I have a table service :
from django.db import models
from users.models import CustomUser
SERVICE_CHOICES = (
('Carpenter', 'Carpenter'),
('Driver', 'Driver'),
('Ambulanve', 'Ambulanve'),
('Spa', 'Spa'),
('Barber', 'Barber'),
('Cleaning', 'Cleaning'),
('Cook', 'Cook'),
)
class Service(models.Model):
name = models.ForeignKey(CustomUser, on_delete=models.CASCADE, limit_choices_to={'is_worker': True},)
service = models.CharField(choices=SERVICE_CHOICES,max_length=100)
def __str__(self):
return f'{self.service} - {self.name}'
and a table CustomUser :
from django.db import models
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
is_worker = models.BooleanField(default=False)
is_customer = models.BooleanField(default=True)
I am serializing the Service table below :
from rest_framework import serializers
from django.contrib.auth.models import User
from .models import *
class ServiceSerializer(serializers.ModelSerializer):
class Meta:
model = Service
fields = '__all__'
But when I add a service from the admin panel, the name shows a number in the browser and not the string. How do I change it to show a the name and not a number?
This is because name is a forignkey to CustomUser, and the default behavior is to return the PK related to the CustomUser instance.
Instead you could use SerializerMethodField.
class UserSerializer(serializers.ModelSerializer):
name = serializers.SerializerMethodField()
class Meta:
model = Service
fields = '__all__'
def get_name(self, obj):
return obj.name.first_name
In your ServiceSerializer you can try:
class ServiceSerializer(serializers.ModelSerializer):
class Meta:
model = Service
fields = '__all__'
def to_representation(self, instance):
rep = super(ServiceSerializer, self).to_representation(instance)
rep['name'] = instance.name.name # instance.name.what_you_use_in_Custom_User_model_to_represent_name
return rep
However, consider whether this is what you want, because this change could lead to more problems than it solves.
Related
I have two models in models.py:
from django.db import models
from django.contrib.auth.models import User
class Course(models.Model):
name = models.CharField(max_length=100)
description = models.TextField()
cost = models.IntegerField()
course_image = models.ImageField()
def __str__(self):
return self.name
class PurchasedCourse(models.Model):
purchased_by = models.ForeignKey(User, on_delete=models.CASCADE)
purchased_course = models.ForeignKey(Course, on_delete=models.CASCADE)
purchased_time = models.DateField(auto_now_add=True, blank=True)
def __str__(self):
return str(self.purchased_by) + ' => ' + str(self.purchased_course)
My serializers.py so far:
from rest_framework import serializers
from .models import *
class CourseSerializer(serializers.ModelSerializer):
class Meta:
model = Course
fields = '__all__'
class PurchasedCourseSerializer(serializers.ModelSerializer):
class Meta:
model = PurchasedCourse
fields = '__all__'
(it is just simple serializing)
My views.py:
from django.http import HttpResponse
from requests import Response
from .models import *
from .serializers import CourseSerializer, PurchasedCourseSerializer
from rest_framework import generics, viewsets
from rest_framework.authentication import BasicAuthentication, TokenAuthentication
from rest_framework.permissions import IsAuthenticated
class CourseViewSet(viewsets.ModelViewSet):
authentication_classes = [TokenAuthentication]
permission_classes = (IsAuthenticated,)
queryset = Course.objects.all()
serializer_class = CourseSerializer
class UserRelatedCourseViewSet(viewsets.ModelViewSet):
queryset = PurchasedCourse.objects.all()
serializer_class = PurchasedCourseSerializer
def get_queryset(self):
return PurchasedCourse.objects.filter(purchased_by__username=self.request.query_params['username'])
What I am getting so far is like { "purchased_by": 5, "purchased_course": 1 } in JSON format.
So, question is how to get all Course objects(with all fields) that depends only on logged in user using PurchasedCourseSerializer?
Something like:
[{
"id": 1;
"username": "testUsername";
course_set
[
{
"id": 2;
"name": "Computer Science";
"description": "Test description text...";
}
{
"id": 5;
"name": "History";
"description": "Test description text...";
}
]
}]
You can also use Nested Serializer. This allows you to explicitly define how the foreign key models should be serialized, and it keeps the logic in one place.
Try this:
class CourseSerializer(serializers.ModelSerializer):
class Meta:
model = Course
fields = "__all__"
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = "__all__"
class PurchasedCourseSerializer(serializers.ModelSerializer):
purchased_course = CourseSerializer()
purchased_by = UserSerializer()
class Meta:
model = PurchasedCourse
fields = "__all__"
Now, you can define the serialization of each foreign key model in its own serializer, and keep the logic organized and maintainable.
You can override the to_represention method in the serializer to change behavior of the field during reading, like this:
class PurchasedCourseSerializer(serializers.ModelSerializer):
username = serializers.CharField(source="purchased_by.username")
def to_representation(self, obj):
self.fields['purchased_course'] = CourseSerializer(obj.purchased_course )
return super().to_representation(obj)
class Meta:
model = PurchasedCourse
fields = '__all__'
FYI, the PurchasedCourse has a FK relation to Course, so one PurchasedCouse will have only one Course object attached to it, hence you can not show the list like representation.
But, if you are serializing from UserSerializer, then you can use the following implementation (through SerializerMethodField):
class UserSerializer(...):
courses = serializer.SerializerMethodField()
def get_courses(self, obj):
return CourseSerializer(Course.objects.filter(purchased_course__purchased_by=obj), many=True).data
You have basically two ways to have all attributes of a foreign key in your serialized JSON.
First solution
Using a to_representation function in your PurchasedCourse serializer, it would look something like this:
class PurchasedCourseSerializer(serializers.ModelSerializer):
class Meta:
model = PurchasedCourse
fields = "__all__"
def to_representation(self, instance):
rep = super().to_representation(instance)
rep["purchased_course"] = CourseSerializer(instance.purchased_course).data
rep["purchased_by"] = UserSerializer(instance.purchased_by).data
return rep
and this basically uses the serializers of your foreign keys to represent those fields with their full data or whatever fields you specify in those serializers.
Second solution
Using nested serializers is basically the same as the first solution but in a more clanky way - at least for me -
class PurchasedCourseSerializer(serializers.ModelSerializer):
purchased_course = CourseSerializer()
purchased_by = UserSerializer()
class Meta:
model = PurchasedCourse
fields = '__all__'
because the first solution gives you an easier and more clear way to manage which fields you particularly need this serializer to return.
I created simple REST API which i want to create product objects. My problem is that API view is not showing me multiple choice field to choose from existing categories.
models.py
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=20)
def __str__(self):
return self.name
class Meta:
ordering = ('name',)
class Product(models.Model):
name = models.CharField(max_length=200, unique=True)
price = models.PositiveSmallIntegerField(default="")
category = models.ForeignKey(Category, on_delete=models.CASCADE, blank=True, default=None)
def __str__(self):
return self.name
class Meta:
ordering = ('name',)
serializers.py
from rest_framework import serializers
from .models import Product, Category
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = '__all__'
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = '__all__'
views.py
from django.shortcuts import render
from django.http import HttpResponse
from rest_framework import viewsets
from .models import Product, Category
from .serilaizers import ProductSerializer, CategorySerializer
from rest_framework.response import Response
from rest_framework.views import APIView
class ProductView(viewsets.ModelViewSet):
queryset = Product.objects.all()
serializer_class = ProductSerializer
class CategoryView(viewsets.ModelViewSet):
queryset = Category.objects.all()
serializer_class = CategorySerializer
That is what i get after running server, only two positions:
__all__ does not work with ForeignKey and other relationship fields. You need to specify the field names explicitly. Add a list or tuple with field names to your ProductSerializer.
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ("name", "price", "category")
I am trying to create nested relationship from more than two models in Django Rest Framework.
Thank you in advance for helping me.
I succeed with two models but when I'm trying with three models unable to create nested serialization.
from django.db import models
class Project(models.Model):
project_id = models.AutoField(primary_key=True)
project_name = models.CharField(max_length=255)
def __str__(self):
return self.name
class Site(models.Model):
site_id = models.AutoField(primary_key=True)
site_name = models.CharField(max_length=255)
project_id= models.ForeignKey(Project, related_name="projectid", on_delete=models.CASCADE)
def __str__(self):
return self.site_name
class Aggin(models.Model):
assign_id = models.AutoField(primary_key=True)
site_id = Models.ForeginKey(Site, relate_name="siteid", on_delete=models.CASCADE)
from rest_framework import serializers
from .models import Song, Artist
class ProjectSerializer(serializers.ModelSerializer):
class Meta:
model = Project
fields = ('__all__')
class SiteSerializer(serializers.ModelSerializer):
class Meta:
model = Site
fields = ('__all__')
class AggignSerializer(serializers.ModelSerializer)
class Meta:
model = Aggin
fields = ('__all__')
I think you don't need to primary id field if you wanna use the Django's default foreign key setting. And related_name should be defined from the view of the counterpart model.
from django.db import models
class Project(models.Model):
project_name = models.CharField(max_length=255)
def __str__(self):
return self.name
class Site(models.Model):
site_name = models.CharField(max_length=255)
project = models.ForeignKey(Project, related_name="sites", on_delete=models.CASCADE)
def __str__(self):
return self.site_name
class Aggin(models.Model):
site = Models.ForeginKey(Site, relate_name="assigns", on_delete=models.CASCADE)
And then, in serializer, you can set like the following.
from rest_framework import serializers
from .models import Song, Artist
class ProjectSerializer(serializers.ModelSerializer):
sites = SiteSerializer(read_only = True, many = True)
class Meta:
model = Project
fields = ('id', 'project_name', 'sites')
class SiteSerializer(serializers.ModelSerializer):
assigns = AggignSerializer(read_only = True, many = True)
class Meta:
model = Site
fields = ('id', 'site_name', 'assigns')
class AggignSerializer(serializers.ModelSerializer):
class Meta:
model = Aggin
fields = ('id')
I have two models CustomUser and Manager_profile given below. There are 2 types of users a manager and a customer. There is a form to register as a manager. How can I set the is_manager field to True when a manager registers?
I also have another question. How can I store the data from department field to the Manager_profile model?
#Models
class CustomUser(AbstractUser):
is_manager = models.BooleanField(default=False)
is_customer = models.BooleanField(default=False)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
def __str__(self):
return self.first_name
class Manager_profile(models.Model):
user = models.OneToOneField(CustomUser,on_delete=models.CASCADE)
department = models.CharField(max_length=100)
def __str__(self):
return self.department
#Form
from django.contrib.auth.forms import UserCreationForm
from account.models import CustomUser
from django import forms
class ManagerCreateForm(UserCreationForm):
department = forms.CharField()
class Meta:
model = CustomUser
fields = ['username','first_name','last_name','department']
I have those models:
models.py
from django.db import models
from django.contrib.auth.models import User
from django.contrib import admin
class Owner(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
account = models.DecimalField(decimal_places=6, max_digits=20)
email = models.EmailField(max_length=70,blank=True)
def __unicode__(self):
return "%s %s" % (self.name,self.last_name)
#staticmethod
def get_by_user(request):
try:
ow = Owner.objects.filter(user_id=request.user.pk)
return ow
except:
return None
class Meta:
db_table = u'backend_owner'
verbose_name_plural = 'Owners'
class Portfolio(models.Model):
name = models.CharField(max_length=100)
owner = models.ForeignKey(Owner, on_delete=models.CASCADE)
def __unicode__(self):
return "%s - %s" % (self.name,self.owner)
admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import User
from backend.models import *
from django.contrib.contenttypes.models import ContentType
# Define an inline admin descriptor for Employee model
# which acts a bit like a singleton
class OwnerInline(admin.StackedInline):
model = Owner
can_delete = False
verbose_name_plural = 'owner'
# Define a new User admin
class UserAdmin(BaseUserAdmin):
inlines = (OwnerInline, )
class PortfolioAdmin(admin.ModelAdmin):
model = Portfolio
list_display = ['name']
def get_queryset(self, request):
qs = super(PortfolioAdmin, self).get_queryset(request)
ow = Owner.get_by_user(request)
if not ow is None and ow.count():
return qs
return qs
# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
admin.site.register(Owner)
admin.site.register(Portfolio, PortfolioAdmin)
Owners is OneToOneField to admin.user => extend class. I want to get a portfolios list for user, so I'm trying to override the queryset.
Error says: type object 'Owner' has no attribute 'get_by_user'
I can't see what's causing that error, but note that your approach is totally unnecessary. All relationships, including OneToOneFields, add a reverse accessor to the related model. So your method can be completely replaced by a simple call to request.user.owner.
(Also, even if you did end up needing some kind of extra functionality, methods that operate on querysets should really go on the Manager class, not as static or class methods on the Model.)