I'm a newbie to Unit testing. As I need to perform test case for following models, serializers, views & urls. Can anyone please help.
Models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.contrib.auth import get_user_model
# Create your models here.
class User(AbstractUser):
"""This Class is used to extend the in-build user model """
ROLE_CHOICES = (('CREATOR','CREATOR'),('MODERATOR','MODERATOR'),('USERS','USERS'))
GENDER_CHOICES = (('MALE','MALE'),('FEMALE',"FEMALE"),('OTHER','OTHER'))
date_of_birth = models.DateField(verbose_name='Date of Birth', null=True)
profile_image = models.ImageField(upload_to='media/profile_images', verbose_name='Profile Image', default='media/profile_images/default.webp', blank=True)
bio = models.TextField(verbose_name='Bio')
role = models.CharField(max_length=10, verbose_name='Role', choices=ROLE_CHOICES)
gender = models.CharField(max_length=6, verbose_name='Gender', choices=GENDER_CHOICES)
Serializers.py
class UserSerializer(serializers.ModelSerializer):
following = serializers.SerializerMethodField()
followers = serializers.SerializerMethodField()
class Meta:
model = User
fields = ('first_name','last_name','username','password','email','date_of_birth',
'profile_image','bio','role','gender', 'following','followers')
extra_kwargs = {'is_active':{'write_only':True},
'password':{'write_only':True}}
def create(self, validated_data):
logger.info('Information Incoming!')
return User.objects.create_user(**validated_data)
def update(self, *args, **kwargs):
user = super().update( *args, **kwargs)
p = user.password
user.set_password(p)
user.save()
return user
def get_following(self, obj):
return FollowingSerializer(obj.following.all(), many=True).data
def get_followers(self, obj):
return FollowersSerializer(obj.followers.all(), many=True).data
Views.py
class UserAPI(viewsets.ModelViewSet):
serializer_class = UserSerializer
queryset = User.objects.all()
# permission_classes = [permissions.IsAuthenticated, TokenHasReadWriteScope]
I tried implementing unit test case but getting an error self.assertEquals(user.get_last_name(), "Johnson") - AttributeError: 'User' object has no attribute 'get_last_name'
tests.py
class UserTests(TestCase):
def setUp(self):
User.objects.create(
first_name='Louis',last_name='Johnson',username='louis.johnson',
email='louis#mail.com',date_of_birth='1994-12-12', bio='Hello I am Louis',
role='MODERATOR',gender='MALE')
def test_users_model(self):
user = User.objects.get(first_name='Louis')
self.assertEquals(user.get_last_name(), "Johnson")
self.assertEquals(user.get_username(), "louis.johnson")
Can anyone please help in sorting out these. Any detailed documentation or help from your end would be a great support.
The user object does not have a function like get_last_name. You can call the attribute directly like that:
def test_users_model(self):
user = User.objects.get(first_name='Louis')
self.assertEquals(user.last_name, "Johnson")
...
Related
I am not very professional in django rest...
I wrote a blog with django rest framework and There is no problem when I want to get all the records related to the Article model or get a specific article, for example
But what I want to do is to send an user id(or an user name) to the view when I click on the user's name.
and as a result display all the records of the Article model related to the request.user and all the records of the Article model related to the user whose name was clicked.
In fact, I want to click on the name of each user, in addition to getting the Articles of that user, the Articles related to the request.user will also be taken
This is what I have done so far...
#models.py
class Article(models.Model):
title = models.CharField(max_length=255)
slug = models.SlugField
author = models.ForeignKey(User , on_delete = models.CASCADE)
content = models.TextField(null = True)
publish = models.DateTimeField(default = timezone.now)
created = models.DateTimeField(auto_now_add = True)
updated = models.DateTimeField(auto_now = True)
status = models.BooleanField(default = False)
def __str__(self):
return self.title
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
pic = models.ImageField(upload_to="img", blank=True, null=True)
def __str__(self):
return self.user.username
#views.py
class ArticleCreate(CreateAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
class ArticleList(ListAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
class ArticleDetail(RetrieveUpdateDestroyAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
class UserDetail(RetrieveUpdateDestroyAPIView):
queryset = get_user_model().objects.all()
serializer_class = UserSerializer
class UserProfile(RetrieveUpdateDestroyAPIView):
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
#serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model()
fields = "__all__"
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = "__all__"
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
exclude = ['updated' , 'created']
You should directly make several modifications in get_queryset() method by using Q objects so:
class ArticleList(ListAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
def get_queryset(self):
user_id = self.kwargs.get('user_id')
if user_id:
articles = Article.objects.filter(Q(author_id=user_id) | Q(author=self.request.user))
return articles
return self.queryset
You'll also need to modify your urls.py file to include the user_id parameter in the URL so:
from django.urls import path
from .views import ArticleList
urlpatterns = [
path('articles/<int:user_id>/', ArticleList.as_view(), name='article_list'),
# ... Other routes.
]
example URL: http://example.com/api/purchases?username=denvercoder9
class ArticleList(ListAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
def get_queryset(self):
username = self.request.query_params.get('username')
if username:
return User.objects.filter(username=username).article_set.all()
user = self.request.user
return Article.objects.filter(author=user)
I'm trying to make my nested serializer to be able to process updating when I'm only updating for the parent model. The default action in drf is that the serializer cant tell if ur child model is trying to updating or create, there's a solution where you just turn off the validation, which I don't think is ideal for the database and iots implication.
I've scour about this topic for like 2 days and I think the ideal approach to this is to override the is_valid method to be able to just return the object.
Idea from :
Django REST Framework ModelSerializer get_or_create functionality
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
class MyModelSerializer(serializers.ModelSerializer):
def is_valid(self, raise_exception=False):
if hasattr(self, 'initial_data'):
# If we are instantiating with data={something}
try:
# Try to get the object in question
obj = Security.objects.get(**self.initial_data)
except (ObjectDoesNotExist, MultipleObjectsReturned):
# Except not finding the object or the data being ambiguous
# for defining it. Then validate the data as usual
return super().is_valid(raise_exception)
else:
# If the object is found add it to the serializer. Then
# validate the data as usual
self.instance = obj
return super().is_valid(raise_exception)
else:
# If the Serializer was instantiated with just an object, and no
# data={something} proceed as usual
return super().is_valid(raise_exception)
class Meta:
model = models.MyModel
The problem is whenever I try to implment I get the error:
Field 'id' expected a number but got [{'id': 18, 'title': 'aDDD', 'body': 'aDDD', 'slug': 'aDDDbabe', 'author': 1, 'question': 25}].
obj = Question.objects.get(**self.initial_data)
This code triggers it, however When I
obj = Question.answers.get(**self.initial_data)
I get 'ReverseManyToOneDescriptor' object has no attribute 'get' .
Since mine is a nested serializer I would have to find another way to make this work. Any advice where I can fix it? :(
serializers.py
from rest_framework import serializers
# https://github.com/encode/django-rest-framework/blob/3.12.4/rest_framework/serializers.py#L839
from rest_framework.fields import ( # NOQA # isort:skip
CreateOnlyDefault, CurrentUserDefault, SkipField, empty
)
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
from .models import Question, Answer
class AnswerSerializer(serializers.ModelSerializer):
"""Serialize Answer model"""
class Meta:
model = Answer
fields = ('id','title', 'body', 'slug', 'author', 'question')
# https://stackoverflow.com/questions/57249850/overwriting-nested-serializers-create-method-throws-typeerror-create-got-mul
read_only_fields = ('question',)
lookup_field = 'slug'
# https://stackoverflow.com/questions/55031552/how-to-access-child-entire-record-in-parent-model-in-django-rest-framework
class QuestionSerializer(serializers.ModelSerializer):
"""Serialize Question model"""
#This answer variable and the fields 'answer' you refer to has to be the SAME but
#These can change exp: 'aaa'
answers = AnswerSerializer(read_only=False, many=True,)
class Meta:
model = Question
fields = ('id','title', 'body', 'slug', 'author', 'category', 'answers',)
lookup_field = 'slug'
def is_valid(self, raise_exception=False):
if hasattr(self, 'initial_data'):
# If we are instantiating with data={something}
try:
# Try to get the object in question
obj = Question.objects.get(**self.initial_data)
except (ObjectDoesNotExist, MultipleObjectsReturned):
print("LOLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL")
return print(obj)
else:
print("LOLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL")
print(obj)
else:
return super().is_valid(raise_exception)
def create(self, validated_data):
answers_data = validated_data.pop('answers')
question = Question.objects.create(**validated_data)
for answer_data in answers_data:
#The above stackoverflow link at the answer serializers is realted to this
Answer.objects.create(question=question, **answer_data)
return question
def update(self, instance, validated_data):
instance.title = validated_data.get('title', instance.title)
instance.body = validated_data.get('body', instance.body)
instance.slug = validated_data.get('slug', instance.slug)
instance.author = validated_data.get('author', instance.author)
instance.category = validated_data.get('category', instance.category)
instance.save()
# https://django.cowhite.com/blog/create-and-update-django-rest-framework-nested-serializers/
answers_data = validated_data.pop('answers')
aas = (instance.answers).all()
print("######")
print(aas)
print("######")
print(instance.answers.get().slug)
print("#########################################")
aas2 = list(aas)
print("######")
for f in validated_data:
print(f)
print(validated_data.items())
print(dir(validated_data))
print("######")
for answer_data in answers_data:
aas3 = aas2.pop(0)
aas3.title= answer_data.get('title', aas3.title)
aas3.body= answer_data.get('body', aas3.body)
aas3.slug= answer_data.get('slug', aas3.slug)
aas3.author= answer_data.get('author', aas3.author)
aas3.question= answer_data.get('question', aas3.question)
aas3.save()
return instance
views.py
from django.shortcuts import render
from .models import Question, Answer
from django.conf import settings
from rest_framework import viewsets
from rest_framework.authentication import TokenAuthentication
from .serializers import QuestionSerializer
#from .permissions import UpdateOwnPrice
from rest_framework.permissions import IsAdminUser
class QuestionViewSet(viewsets.ModelViewSet):
"""CRUD """
serializer_class = QuestionSerializer
queryset = Question.objects.all()
authentication_classes = (TokenAuthentication,)
#permission_classes = (UpdateOwnPrice,)
lookup_field = 'slug'
extra_kwargs = {'slug': {'validators': []},}
models.py
from django.db import models
from django.conf import settings
from django.db.models import Q
from django.utils import timezone
from django.urls import reverse
class Category(models.Model):
name= models.CharField(max_length=100)
def __str__(self):
return self.name
class Question(models.Model):
title= models.CharField(max_length= 100)
body= models.TextField()
slug= models.SlugField(unique= True)
date_posted= models.DateTimeField(default=timezone.now)
author= models.ForeignKey(settings.AUTH_USER_MODEL, on_delete= models.CASCADE, related_name = 'questions')
category= models.ForeignKey(Category, on_delete= models.CASCADE, related_name = 'questions')
#objects= QnAManager()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('home')
class Answer(models.Model):
title= models.CharField(max_length= 100)
body= models.TextField()
slug= models.SlugField(unique= True)
date_posted= models.DateTimeField(default=timezone.now)
author= models.ForeignKey(settings.AUTH_USER_MODEL, on_delete= models.CASCADE, related_name = 'answers')
question= models.ForeignKey(Question, on_delete= models.CASCADE, related_name = 'answers')
#objects= QnAManager()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('home')
I am having problems with reproducing example from Julia Elman's book
models.py
class Sprint(models.Model):
name = models.CharField(max_length=100, blank=True, default='')
description = models.TextField(blank=True, default='')
end = models.DateField(unique=True)
def __str__(self):
return self.name or _('Sprint ending %s') % self.end
serializer.py
from rest_framework import serializers
from django.contrib.auth import get_user_model
from rest_framework.reverse import reverse
from .models import Sprint, Task
User = get_user_model()
class SprintSerializer(serializers.ModelSerializer):
links = serializers.SerializerMethodField('get_links')
class Meta:
model = Sprint
fields = ('id', 'name', 'description', 'end', 'links',)
def get_links(self, obj):
request = self.context['request']
return {'self': reverse('sprint-detail',kwargs={'pk': obj.pk},request=request),}
views.py
from django.contrib.auth import get_user_model
from rest_framework import authentication, permissions, viewsets
from .models import Sprint,Task
from .serializers import SprintSerializer,TaskSerializer, UserSerializer
User = get_user_model()
class DefaultsMixin(object):
authentication_classes = (authentication.BasicAuthentication,authentication.TokenAuthentication,)
permission_classes = (permissions.IsAuthenticated,)
paginate_by = 25
paginate_by_param = 'page_size'
max_paginate_by = 100
class SprintViewSet(DefaultsMixin, viewsets.ModelViewSet):
queryset = Sprint.objects.order_by('end')
serializer_class = SprintSerializer
I try to see repr from shell
from board.serializers import SprintSerializer
>>> s = SprintSerializer()
>>> print (repr(s))
But I have problem
AssertionError: The field 'links' was declared on serializer SprintSerializer, but has not been included in the 'fields' option.
My DRF
print (rest_framework.VERSION)
3.8.2
How to debug this issue?
Yes,what Alexandr Tartanov suggested works fine.We need to pass arguments with source
links = serializers.SerializerMethodField(source='get_links')
Output
print (repr(s))
SprintSerializer():
id = IntegerField(label='ID', read_only=True)
name = CharField(allow_blank=True, max_length=100, required=False)
description = CharField(allow_blank=True, required=False, style={'base_template': 'textarea.html'})
end = DateField(validators=[<UniqueValidator(queryset=Sprint.objects.all())>])
links = SerializerMethodField(source='get_links')
I have a simple model where a User can take part in multiple Game and a Game can be played by multiple User. Each User has a score field in a Game.
To achieve this, I have followed the example provided in the Django documentation to implement such a ManyToMany relationship with an additional field by creating a third model Membership to link the Game table and the User one.
I have the following models.py:
from django.contrib.auth.models import AbstractUser
from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token
from django.db import models
class User(AbstractUser):
pass
# triggered as soon as a new user is saved in the db
#receiver(post_save, sender=User)
def create_auth_token(sender, instance=None, created=False, **kwargs):
if created:
Token.objects.create(user=instance)
class Game(models.Model):
users = models.ManyToManyField(User, through='Membership')
class Membership(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
game = models.ForeignKey(Game, on_delete=models.CASCADE)
score = models.IntegerField()
Then I have written serializers.py:
from rest_framework import serializers
from .models import User, Membership, Game
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
# unique email
model._meta.get_field('email').__dict__['_unique'] = True
fields = ('id', 'username', 'password', 'email')
def create(self, validated_data):
user = super().create(validated_data)
if 'password' in validated_data:
user.set_password(validated_data['password'])
user.save()
return user
class GameSerializer(serializers.ModelSerializer):
users = UserSerializer(many=True)
class Meta:
model = Game
fields = ('users',)
class MembershipSerializer(serializers.HyperlinkedModelSerializer):
id = serializers.ReadOnlyField(source='game.id')
class Meta:
model = Membership
fields = ('id', 'score')
My goal is to be able to save a new Game by posting something like:
{
{user_id = 1,
score = 25},
{user_id = 2,
score = 10}
}
I am thus wondering how to achieve this.
To achieve this you can use Django generic views where you can have CreateAPI view ,where after each game you can create an membership object.
But you have to use a ModelSerializer instead of HyperlinkedModelSerializer.
Do this manually as:
serializers.py
class MembershipSerializer(serializers.ModelSerializer):
class Meta:
model = Membership
fields = ('user ', 'game', 'score')
views.py
class GameViewSet(ModelViewSet):
queryset = Game.objects.all()
serializer_class = GameSerializer
permission_classes = (IsAuthenticated,)
#detail_route(methods=['POST'])
def bulk_create(self, request, pk):
game = self.get_object()
datas = request.data.getlist('data')
import ast
for _data in datas:
data = ast.literal_eval(_data)
data['game'] = game.id
serializer = MembershipSerializer(data=data)
if serializer.is_valid()
serializer.save()
else:
pass
return Response('success')
the data you post is something like {'data':[{user_id = 1,score = 25}]}
the post url is /game/your_game_id/bulk_create/,
not test code,your may get some error,but you can achieve like this.
I am trying to save data from my Django Admin to my database but somehow it is not happening. I have created a form in one of my apps which my admin uses.I am new to Django and any help would be greatly appreciated.
Below is the relevant code:
models.py
from __future__ import unicode_literals
from django.contrib.auth.models import User
from django_countries.fields import CountryField
from django.db import models
class UserProfile(models.Model):
user = models.OneToOneField(User)
name = models.CharField(max_length=100)
bio = models.TextField(max_length=1000, blank=True, null=True)
image = models.FileField()
country = CountryField()
city = models.CharField(max_length=100)
twitter = models.CharField(max_length=100, null=True, blank=True)
linkedin = models.CharField(max_length=100, null=True, blank=True)
location = models.TextField()
def __unicode__(self):
return self.name
def __str__(self):
return self.name
class Mentor(models.Model):
mentor = models.CharField(max_length=100)
mentee = models.CharField(max_length=100)
def __unicode__(self):
return self.mentee
def __str__(self):
return self.mentee
forms.py
from django import forms
from models import UserProfile, Mentor
from django_countries.fields import CountryField
from django.contrib.auth.models import User
from reports.models import Reports
class UserProfileForm(forms.ModelForm):
name = forms.CharField(max_length=100)
bio = forms.Textarea()
image = forms.FileField(label='Profile Photo')
country = CountryField(blank_label='(Select Country)')
city = forms.CharField(max_length=100)
twitter = forms.CharField(max_length=100, required=False)
linkedin = forms.CharField(max_length=100, required=False)
class Meta:
model = UserProfile
exclude = ('user',)
class MentorForm(forms.ModelForm):
mentor_choices = tuple(UserProfile.objects.filter(user__is_staff=1).order_by('name').values_list('name', 'name'))
mentee_choices = tuple(UserProfile.objects.exclude(user__is_staff=1).order_by('name').values_list('name', 'name'))
mentor_name = forms.ChoiceField(choices=mentor_choices)
mentee_name = forms.ChoiceField(choices=mentee_choices)
def save(self, commit=True):
mentor_name = self.cleaned_data.get('mentor_name', None)
mentor_name = self.cleaned_data.get('mentee_name', None)
return super(MentorForm, self).save(commit=commit)
class Meta:
model = Mentor
fields= ('mentor_name', 'mentee_name')
admin.py
from django.contrib import admin
from .models import UserProfile, Mentor
from.forms import MentorForm
class UserProfileAdmin(admin.ModelAdmin):
list_display = ('user',)
search_fields = ['user']
def save_model(self, request, obj, form, change):
obj.created_by = request.user
obj.save()
admin.site.register(UserProfile, UserProfileAdmin )
class MentorAdmin(admin.ModelAdmin):
list_display = ('__unicode__','mentee')
search_fields = ['mentee', 'mentor']
form = MentorForm
fieldsets = (
(None,{
'fields': ('mentor_name', 'mentee_name'),
}),
)
def save_model(self, request, obj, form, change):
super(MentorAdmin, self).save_model(request, obj, form, change)
admin.site.register(Mentor, MentorAdmin )
The UserProfile works perfectly but the Mentor form in admin doesn't save anything in the database. It creates blank entries into the database, so I know that the front and backend are talking but no data is being passed. Any help will be very helpful
def save(self, commit=True):
# here you define `mentor_name`. OK.
mentor_name = self.cleaned_data.get('mentor_name', None)
# here you redefine `mentor_name`. I guess it is a typo and should be `mentee_name`.
mentor_name = self.cleaned_data.get('mentee_name', None)
# And... you don't do anything with these variables.
return super(MentorForm, self).save(commit=commit)
This method is equivalent to:
def save(self, commit=True):
return super(MentorForm, self).save(commit=commit)
Which is equivalent to not overriding the save method at all.
And what about this?
def save_model(self, request, obj, form, change):
super(MentorAdmin, self).save_model(request, obj, form, change)
What is the purpose of overriding a method and only calling the parent method with the exact same arguments?
But the actual issue is here:
mentor_choices = tuple(UserProfile.objects.filter(user__is_staff=1).order_by('name').values_list('name', 'name'))
mentee_choices = tuple(UserProfile.objects.exclude(user__is_staff=1).order_by('name').values_list('name', 'name'))
mentor_name = forms.ChoiceField(choices=mentor_choices)
mentee_name = forms.ChoiceField(choices=mentee_choices)
class Meta:
model = Mentor
fields = ('mentor_name', 'mentee_name')
You use a ModelForm but none of the Mentor's fields is in fields. What are you expecting this to do other than saving a row with Mentor.mentor = None and Mentor.mentee = None. You don't even mention those fields.
And why are you using CharField for Mentor.mentor and Mentor.mentee while you likely want a foreign key.
class Mentor(models.Model):
mentor = models.ForeignKey(UserProfile, models.PROTECT,
related_name='mentees')
mentee = models.ForeignKey(UserProfile, models.PROTECT,
related_name='mentors')
class MentorForm(forms.ModelForm):
class Meta:
model = Mentor
fields = ('mentor', 'mentee')
mentor = forms.ModelChoiceField(queryset=UserProfile.objects.filter(
user__is_staff=True).order_by('name'))
mentee = forms.ModelChoiceField(queryset=UserProfile.objects.exclude(
user__is_staff=True).order_by('name'))
Or even better:
class Mentor(models.Model):
mentor = models.ForeignKey(
UserProfile, models.PROTECT, related_name='mentees',
limit_choices_to={'user__is_staff': True},
)
mentee = models.ForeignKey(
UserProfile, models.PROTECT, related_name='mentors',
limit_choices_to={'user__is_staff': False},
)
Which avoids you to create a form.