I am new to Django Rest Framework. I have been using regular Django I want something similar to Django's CreateView for this model.
Intro: The superuser is the only user. All patients belong to the user and Embryos belong to the patient. One patient can have multiple embryos. Below are my models
class Patients(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
first_name = models.CharField(max_length=25)
last_name = models.CharField(max_length=35)
email = models.EmailField(unique=True)
class Embryo(models.Model):
patient = models.ForeignKey(Patients, on_delete=models.CASCADE)
code_name = models.CharField(max_length=100)
karyotype = models.CharField(max_length=100)
GENDER_CHOICES = (
("M", "Male"),
("F", "Female"),
)
sex = models.CharField(max_length=1, choices=GENDER_CHOICES)
I am trying to make create views for my models.
Below is what I have so far
class PatientsApiView(viewsets.ModelViewSet):
"""Handles Creating, reading and updating Patients"""
serializer_class = serializers.PatientsSerializer
queryset = Patients.objects.all()
authentication_classes = (TokenAuthentication,)
filter_backends = (filters.SearchFilter,)
search_fields = ("first_name", "last_name", "phone", "email",)
def form_valid(self, form, *args, **kwargs):
self.object = form.save(commit=False)
self.object.user = self.request.user
self.object.save()
return super().form_valid(form)
"""How can I get the above with Django Rest Framework. Can I do:
def __getattr__(self, user):
return self.request.user
"""
Below is the viewset for EmbroApiView
class EmbroApiView(viewsets.ModelViewSet):
"""Handles Creating, reading and updating Patients"""
serializer_class = serializers.EmbryoSerializer
queryset = Embryo.objects.all()
authentication_classes = (TokenAuthentication,)
filter_backends = (filters.SearchFilter,)
search_fields = ("code_name", "karyotype", "sex", "down_syndrome",)
def form_valid(self, form, *args, **kwargs):
self.object = form.save(commit=False)
pk = self.kwargs.get('pk')
self.object.patient = get_object_or_404(Patient, pk=pk)
self.object.save()
return super().form_valid(form)
"""How can I get the above with Django Rest Framework. Can I do:
def __getattr__(self, patient):
return get_object_or_404(Patients, pk=self.patient.pk)
"""
According to #Daniel Roseman solution. Have I done this correctly?
class PatientsApiView(viewsets.ModelViewSet):
"""Handles Creating, reading and updating Patients"""
serializer_class = serializers.PatientsSerializer
queryset = Patients.objects.all()
authentication_classes = (TokenAuthentication,)
filter_backends = (filters.SearchFilter,)
permission_classes = (IsAuthenticated,)
search_fields = ("first_name", "last_name", "phone", "email",)
def perform_create(self, serializer):
serializer.save(user=self.request.user)
class EmbroApiView(viewsets.ModelViewSet):
"""Handles Creating, reading and updating Patients"""
serializer_class = serializers.EmbryoSerializer
queryset = Embryo.objects.all()
authentication_classes = (TokenAuthentication,)
filter_backends = (filters.SearchFilter,)
permission_classes = (IsAuthenticated,)
search_fields = ("code_name", "karyotype", "sex", "down_syndrome",)
def perform_create(self, serializer):
serializer.save(patient_id=self.kwargs['pk'])
I'm not sure why you mention __getattr__ in the comment, that would have nothing to do with this.
The hooks for extra actions on create and update are called perform_create and perform_update. So as the docs show, you can do:
def perform_create(self, serializer)
serializer.save(user=self.request.user)
etc.
Related
now i'm studying DRF and have to do project with photo albums. One of my tasks is to create custom #action "patch" method, using model field "title", but i can't understand how to add fields for search in custom methods. We can see them in base methods, like "get", "patch" and "put", but i can't find any info about how to add them to custom actions.
If anyone knows how to do it, please, tell me.
My model:
class PhotoAlbum(models.Model):
title = models.CharField(verbose_name='Название альбома', max_length=50, null=True)
created_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, verbose_name='Автор')
created_at = models.DateTimeField(verbose_name='Дата создания', editable=False,
default=datetime.datetime.today())
class Meta:
verbose_name = 'Фотоальбом'
verbose_name_plural = 'Фотоальбомы'
def __str__(self):
return f'{self.title} Автор: {self.created_by} Дата: {self.created_at}'
My view:
def photo_album_view(request):
photo_albums = PhotoAlbum.objects.all()
context = {
'photo_albums': photo_albums,
}
return render(request, 'photo_album.html', context=context)
My viewset:
class AlbumFilter(django_filters.FilterSet):
title = django_filters.Filter(field_name='title')
class PhotoAlbumViewSet(viewsets.ModelViewSet):
queryset = PhotoAlbum.objects.all()
filterset_class = AlbumFilter
serializer_class = PhotoAlbumSerializer
pagination_class = ResultPagination
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
search_fields = ['title', ]
ordering_fields = ['created_at', ]
To answer the above
say you have a viewset class
// all imports
class AbcApi(viewsets.ModelViewset):
serializer_class = random
permission_classses = [IsAuthenticated]
search_fields = ["a_field", "b_field"]
filter_backends = [....]
#custom action
#action(detail=False)
def custom_action(self, *args, **kwargs):
""" to answer your question """
qset_ = **self.filter_queryset**(self.queryset())
#the filter bold automatically involves the class filters in the custom method
the answer is to use self.filter_queryset(..pass the get_queryset()) here as seen
If you are using ModelViewSet, I believe you are looking for one of these custom methods:
def update(self, request, pk=None):
pass
def partial_update(self, request, pk=None):
pass
These functions allow you to add custom functionalities to your update method. Reference
Brief example:
def partial_update(self, request, pk=None):
serializer = UserPostSerializer(user, data=request.data, partial=True)
if serializer.is_valid():
try:
serializer.save()
except ValueError:
return Response({"detail": "Serializer not valid."}, status=400)
return Response({"detail": "User updated."})
else:
return Response(serializer.errors)
I have been spending quite a lot of time trying to understand this error I got.
From models.py
class Company(models.Model):
name = models.CharField(max_length=100, unique=True, default="")
email = models.CharField(max_length=100, unique=True, default="")
password = models.CharField(max_length=100, unique=True, default="")
bsb = models.IntegerField(default=0)
account = models.IntegerField(default=0)
sign_up_date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class Payment(models.Model):
company = models.ForeignKey(Company, on_delete=models.CASCADE, related_name="payments")
name = models.CharField(max_length=100, unique=True, default="")
bsb = models.IntegerField(default=0)
account = models.IntegerField(default=0)
created_date = models.DateTimeField(auto_now_add=True)
paid_date = models.DateTimeField(default=datetime.now() + timedelta(days=36500))
status = models.IntegerField(default=0)
and from serializers.py
from rest_framework import serializers
from .models import Payment, Company
class PaymentSerializer(serializers.ModelSerializer):
company = serializers.ReadOnlyField(source='company.name')
class Meta:
model = Payment
fields = ['company', 'name', 'bsb', 'account', 'created_date', 'paid_date', 'status']
class CompanySerializer(serializers.ModelSerializer):
payments = serializers.PrimaryKeyRelatedField(many=True, queryset=Payment.objects.all())
class Meta:
model = Company
fields = ["name", "email", "password", "payments",
"bsb", "account", "sign_up_date"]
As you can see, I have included the related_name as "payments" for company attribute in Payment class. But when I go to http://localhost:8000/bill_payer/resources/company I got the following error:
AttributeError: 'User' object has no attribute 'payments'
I have verified that my Company class do indeed have the payments class through manage.py shell.
Any idea? I am new. Here's views.py in case it is important:
class PaymentList(APIView):
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def get(self, request):
payments = Payment.objects.all()
serializer = PaymentSerializer(payments, many=True)
return Response(serializer.data)
def perform_create(self, serializer):
serializer.save(company=self.request.user)
class PaymentDetail(APIView):
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def get_object(self, pk):
try:
return Payment.objects.get(pk=pk)
except Payment.DoesNotExist:
raise Http404
def get(self, request, pk):
payment = self.get_object(pk)
serializer = PaymentSerializer(payment)
return Response(serializer.data)
def put(self, request, pk):
payment = self.get_object(pk)
serializer = PaymentSerializer(payment, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk):
payment = self.get_object(pk)
payment.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
class CompanyList(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = CompanySerializer
class CompanyDetail(generics.RetrieveAPIView):
queryset = User.objects.all()
serializer_class = CompanySerializer
You get the error because you try to use User objects for CompanySerializer which expects Company model objects. It will work if you fix it as below.
class CompanyList(generics.ListAPIView):
queryset = Company.objects.all()
serializer_class = CompanySerializer
class CompanyDetail(generics.RetrieveAPIView):
queryset = Company.objects.all()
serializer_class = CompanySerializer
Please change this
class CompanyList(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = CompanySerializer
class CompanyDetail(generics.RetrieveAPIView):
queryset = User.objects.all()
serializer_class = CompanySerializer
to
class CompanyList(generics.ListAPIView):
queryset = Company.objects.all()
serializer_class = CompanySerializer
class CompanyDetail(generics.RetrieveAPIView):
queryset = Company.objects.all()
serializer_class = CompanySerializer
also your doing wrong here in perform_create method.
class PaymentList(APIView):
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def get(self, request):
payments = Payment.objects.all()
serializer = PaymentSerializer(payments, many=True)
return Response(serializer.data)
def perform_create(self, serializer):
serializer.save(company=self.request.user)
it should not be
serializer.save(company=self.request.user)
coz its not a company object. self.request.user is a user object. And you are trying to save user object to company object. its wrong.
try using
serializer.save()
or
serializer.save(company=serializer.data.get("company"))
I have a below view, serializer and model. Using BrowsableAPIRenderer i render HTML page. I need to assign current user id to user_id field and hide the same from HTML rendering(not allow to edit and enter). I am using Django rest framework 2.3.13. Kindly help me to achieve this.
serializer.py
class TagOwnerSerializer(serializers.HyperlinkedModelSerializer):
tag_id = serializers.SlugRelatedField(slug_field='tag_id')
user_id = serializers.IntegerField(write_only=True)
class Meta:
model = TagOwner
fields = ('url','tag_id','start_time','end_time','user_id')
views.py
class TagOwnerViewSet(viewsets.ModelViewSet):
"""
TagOwner table view set.
"""
model = TagOwner
queryset = TagOwner.objects.all()
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
serializer_class = TagOwnerSerializer
renderer_classes = (BrowsableAPIRenderer, JSONRenderer,JSONPRenderer,XMLRenderer,YAMLRenderer)
filter_backends = (filters.DjangoFilterBackend, filters.SearchFilter ,filters.OrderingFilter)
filter_class = TagOwnerFilter
#search_fields = ('tag_id',)
ordering_fields = '__all__'
def get_queryset(self):
user = self.request.user
if self.request.user.is_authenticated():
if not user:
return []
return TagOwner.objects.filter(user_id=user.id)
public_tags = TagReads.objects.filter(public=True).values_list('tag_id').distinct()
return TagOwner.objects.filter(tag_id__in=public_tags)
def create(self,request):
serializer = self.serializer_class(data=request.DATA)
if serializer.is_valid():
user=self.request.user.id
serializer.save(user_id = user)
return Response(serializer.data, status = status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
models.py
class TagOwner(models.Model):
user_id = models.IntegerField(blank=False,db_column='user_id',)
tag_id = models.OneToOneField('Tags',on_delete=models.CASCADE,primary_key=True,db_column='tag_id',)
start_time = models.DateTimeField(blank=True, null=True)
end_time = models.DateTimeField(blank=True, null=True)
class Meta:
managed = False
db_table = 'tag_owner'
Found it myself.
serializer.py
class TagOwnerSerializer(serializers.HyperlinkedModelSerializer):
tag_id = serializers.SlugRelatedField(slug_field='tag_id')
user_id = serializers.Field(source='user_id')
#user_id = serializers.PrimaryKeyRelatedField(read_only=True,default=serializers.CurrentUserDefault())
class Meta:
model = TagOwner
fields = ('url','tag_id','start_time','end_time','user_id')
views.py
class TagOwnerViewSet(viewsets.ModelViewSet):
"""
TagOwner table view set.
"""
model = TagOwner
queryset = TagOwner.objects.all()
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
serializer_class = TagOwnerSerializer
renderer_classes = (BrowsableAPIRenderer, JSONRenderer,JSONPRenderer,XMLRenderer,YAMLRenderer)
filter_backends = (filters.DjangoFilterBackend, filters.SearchFilter ,filters.OrderingFilter)
filter_class = TagOwnerFilter
#search_fields = ('tag_id',)
ordering_fields = '__all__'
def get_queryset(self):
user = self.request.user
if self.request.user.is_authenticated():
if not user:
return []
return TagOwner.objects.filter(user_id=user.id)
public_tags = TagReads.objects.filter(public=True).values_list('tag_id').distinct()
return TagOwner.objects.filter(tag_id__in=public_tags)
def pre_save(self, obj):
obj.user_id = self.request.user.id
This worked perfectly for me.
I'm new in Django and DRF and I'm trying to create a new user through POST request. I send the username and password parameters but django doesn't identify the parameters inside the POST request.
My serializer:
class UserSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True)
def create(self, validated_data):
user=get_user_model().objects.create(
username = validated_data['username']
)
user.set_password(validated_data['password'])
user.save()
return user
class Meta:
model = get_user_model()
fields = ('username', 'password')
My view class:
class CreateUserView(CreateAPIView):
model = get_user_model()
permission_classes = (AllowAny,)
serializer_class = UserSerializer
I have tried to use serializers.ModelSerializer and serializer.Serializers but don't have success.
class UserSerializer(serializers.ModelSerializer):
def create(self, validated_data):
user = UserModel.objects.create_user(**validated_data)
return user
class Meta:
model = YOUR_MODEL
fields = ('username', 'password',)
class CreateUserView(CreateAPIView):
model = YOUR_MODEL.objects.all()
permission_classes = (AllowAny,)
serializer_class = UserSerializer
You can do View as this and check your errors.
view.py
class CreateUser(APIView):
permission_classes = (AllowAny,)
def post(self, request):
post_data = request.data
print post_data # See what is your post DATA
serializer = UserSerializer(data=post_data)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response(data={'user':serializer.data})
return Response(data={'user':serializer.errors})
I copied https://pypi.python.org/pypi/fh-drf-friendship/0.1.1 (Because i don't know how to apply fh-drf-friendship on my project.)
views.py
class FriendViewSet(NestedViewSetMixin, mixins.ListModelMixin, mixins.CreateModelMixin,
mixins.DestroyModelMixin, viewsets.GenericViewSet):
serializer_class = UserSerializer
def create(self, request, *args, **kwargs):
request.data['to_user'] = self.get_parents_query_dict()['user']
serializer = FriendSerializer(data=request.data)
if serializer.is_valid():
instance = serializer.save()
headers = self.get_success_headers(serializer.data)
serializer = FriendReadSerializer(instance)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def get_object(self):
to_user = User.objects.get(pk=self.get_parents_query_dict().get('user'))
from_user = User.objects.get(pk=self.kwargs.get('pk'))
return Friend.objects.get(to_user=to_user, from_user=from_user)
serializers.py
class UserSerializer(serializers.ModelSerializer):
related_postwriter = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
related_commentwriter = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = User
fields = ('id', 'username', 'email', 'related_postwriter', 'related_commentwriter')
class FriendSerializer(rest_framework_common.serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(source='from_user', queryset=User.objects.all())
class Meta:
model = Friend
exclude = ('url', 'from_user',)
def create(self, validated_data):
fr = FriendshipRequest.objects.get(from_user=validated_data['from_user'], to_user=validated_data['to_user'])
if fr and fr.accept():
return Friend.objects.get(from_user=validated_data['from_user'], to_user=validated_data['to_user'])
class FriendReadSerializer(rest_framework_common.serializers.ModelSerializer):
user = UserSerializer(source='from_user')
created_at = serializers.DateTimeField(source='created')
class Meta:
model = Friend
exclude = ('url', 'from_user', 'to_user', 'created',)
django version == 1.9.7
django rest framework version == 3.4.1
python3 == 3.5
On urls.py, there are router.register(r'friend', views.FriendViewSet, base_name='friend')
And when i go http://127.0.0.1:8000/friend/ on web browser,
DoesNotExist at /friend/
User matching query does not exist. comes.
How can i solve it?