I am new to django testing and have some issues using them to test relationship between models.
Here is an extract of my models:
class Member(models.Model):
user = models.OneToOneField('auth.User')
points = models.IntegerField()
def __unicode__(self):
return self.points
def get_number_of_poll(self):
nbr_of_poll = Poll.objects.filter(user=self.user).count()
return nbr_of_poll
class Poll(models.Model):
question = models.CharField(max_length=200)
user = models.ForeignKey(User)
def __unicode__(self):
return self.question
And here are the tests:
from polls.models import Member, Poll
from django.contrib.auth.models import User
from django.test import TestCase
class MemberTestCase(TestCase):
def setUp(self):
user = User(username='user')
self.member = Member(user=user, points=5000)
poll = Poll(question='poll', user=user)
def test_get_number_of_poll(self):
self.assertEqual(self.member.get_number_of_poll(), 1)
The issue is with the test_get_number_of_poll() method that always returns 0. The code works as expected on the site.
Am I doing something wrong with the test? I am not sure how I am supposed to set the poll in the test class.
You don't save any of the items you create in your setUp method. Instantiating a model object doesn't save it to the database: you should either call save() on them, or just use User.objects.create(username='user') etc which does the save for you.
The problem is that
poll = Poll(question='poll', user=user)
Only instantiates the Poll object, use the manager to actually save the object, e.g.
poll = Poll.objects.create(question='poll', user=user)
Related
I want to update my model upon login (to check the authorizations of a person from an external system).
The code of my model looks as follow:
import json
from django.contrib.auth.models import AbstractUser
from django.contrib.auth.signals import user_logged_in
from django.db import models
class Person(AbstractUser):
is_dean = models.BooleanField(null=False, blank=False, default=False)
is_institute_manager = models.BooleanField(null=False, blank=False, default=False)
managed_institutes = models.TextField(blank=True, null=True, default=None)
def get_managed_institutes(self):
return json.loads(self.managed_institutes)
def set_managed_institutes(self, value):
self.managed_institutes = json.dumps(value)
# Signals processing
def check_authorizations(sender, user, request, **kwargs):
...
# check if the user is dean
is_dean = False
# logic to check if the user is dean...
user.is_dean = is_dean
# Check if the user manages institutes
is_institute_manager = False
managed_institutes = list()
# Logic to check if the user is managing institutes ...
user.is_institute_manager = is_institute_manager
user.set_managed_institutes = managed_institutes
user.save()
user_logged_in.connect(check_authorizations)
Surprisingly, the boolean flags get set correctly, but the method set_managed_institute never gets called...
I am quite convinced this a trivial mistake from my end, but I can't figure it out.
That is not how you call methods in Python. You need to do so explicitly:
user.set_managed_institutes(managed_institutes)
Or did you mean to define a property?
#property
def managed_institutes(self):
return json.loads(self._managed_institutes)
#managed_institutes.setter
def managed_institutes(self, value):
self._managed_institutes = json.dumps(value)
But also note, you probably want to use a JsonField anyway. If you're using PostgreSQL, there is one defined in Django directly; otherwise there are several third-party libraries that take care of serializing and deserializing your data on load/save.
I am trying to post to my API with foreign key relationships. It's throwing me back an error saying it's expecting a dictionary as opposed to int for character, character_opponent and stage. This is because the way my models are set up. They have foreign key relationships. The model in question looks like this:
import uuid
from django.db import models
from django.utils import timezone
from analysis.models import Analysis
from characters.models import Character
from stages.models import Stage
class Match(models.Model):
analysis = models.ForeignKey(Analysis, on_delete=models.CASCADE)
character = models.ForeignKey(Character, on_delete=models.CASCADE, related_name='character')
character_won = models.BooleanField()
character_opponent = models.ForeignKey(Character, on_delete=models.CASCADE, related_name='character_opponent')
character_opponent_won = models.BooleanField()
created_at = models.DateTimeField(editable=False)
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
updated_at = models.DateTimeField(editable=False)
stage = models.ForeignKey(Stage, on_delete=models.CASCADE)
def __str__(self):
return '%s vs. %s on %s' % (self.character, self.character_opponent, self.stage)
def save(self, *args, **kwargs):
''' On save, update timestamps '''
if not self.created_at:
self.created_at = timezone.now()
self.updated_at = timezone.now()
return super(Match, self).save(*args, **kwargs)
class Meta:
db_table = "matches"
And here is my serializer:
from rest_framework import serializers
from matches.models import Match
from characters.serializers import CharacterSerializer
from stages.serializers import StageSerializer
class MatchSerializer(serializers.ModelSerializer):
character = CharacterSerializer()
character_opponent = CharacterSerializer()
stage = StageSerializer()
class Meta:
model = Match
fields = ('id', 'analysis', 'character', 'character_won', 'character_opponent', 'character_opponent_won', 'stage')
Is there some option I am missing here to be able to post properly? Clearly I shouldn't have to pass the entire character object each time I want to post something, right? I should just be able to pass the primary key.
From your few comments I understood that you need nested serializer in GET method. What I suggest is, use two[or more] serializers for your API class.
Assuming you are using ModelViewSet API class is using,then you could override get_serializer_class() method as below,
from rest_framework.viewsets import ModelViewSet
class MatchAPI(ModelViewSet):
queryset = Match.objects.all()
def get_serializer_class(self):
if self.action == 'create':
return MatchCreateSerializer
return MatchSerializer
And your MatchCreateSerializer will be like this,
class MatchCreateSerializer(serializers.ModelSerializer):
class Meta:
fields = '__all__'
model = Match
Thus, you only need to provide the PKs of analysis,character etc while creation of Match instance
It will come down to your CharacterSerializer and StageSerializer. If you want to input 1 format (using serialisers.PrimaryKeyRelatedField()), but output another (CharacterSerializer, StageSerializer), you might be best served using 2 serialisers and switching in your view.
In your view you can override get_serializer_class and check your request method, or in the case of a viewset you can check the method being invoked.
When you declare a serializer related field using another serializer, like this
character = CharacterSerializer()
you are telling django-rest-framework that you want a nested serializer. What you want is something like this instead
character = serializers.PrimaryKeyRelatedField()
or you can actually just leave the explicit field declaration out of the serializer (since this is the default), see the doc on serializer relations.
I am a new in Django world and I want to link two classes from models.py so that i can set their variables equal to each other. Here is the models.py code:
from django.db import models
from django.core.urlresolvers import reverse
# Create your models here.
class file(models.Model):
title = models.CharField(max_length=250)
FILE_TYPE_CHOICES = (
('audio','Audio'),
('games','Games'),
('videos','Videos'),
('applications','Applications'),
('books','Books/Docs'),
('others','Others')
)
file_type = models.CharField(max_length=10,choices=FILE_TYPE_CHOICES,default='others')
description = models.TextField(max_length=6000)
#uploader_username = ???
def get_absolute_url(self):
return reverse('one:user')
def __str__(self):
return self.title
class user (models.Model):
username= models.CharField(max_length=100)
email=models.EmailField
password= models.CharField(max_length = 100)
user_files = models.ForeignKey(file, on_delete=models.CASCADE)
Here I want to set uploader_username from file class equals tousername from user class.
No, you don't want to do this. You want a ForeignKey from File to User, not the other way round, then you can just access my_file.user.username.
Note, it is a bad idea to define your own user class like this. There can be good reasons for doing so, but if so you must inherit from the abstract base classes in the auth app; failure to do so is a serious security problem as you will be storing passwords in clear text. It doesn't look like you need your own model here; you should remove this class.
I want to override the built-in django .save() method to perform a check against all other objects in the database.
For example:
class User(models.Model):
name = models.CharField(max_length=120)
class Admin(models.Model):
name = models.CharField(max_length=120)
class SecurityGroup(models.Model):
name = models.CharField(max_length=120)
users = models.ManytoManyField(User)
admins = models.ManytoManyField(Admin)
def save(self, *args, **kwargs):
# check admins don't exist in any other SecurityGroup prior to save
super(SecurityGroup, self).save(*args, **kwargs) # Call the "real" save() method.
The documentation example is pretty simple, and doesn't describe this type of pre-save check.
I have tried adding in lines to .save() such as:
`self.objects.filter(admins__name=self.admins.name).count()`
to call the other SecurityGroup objects but I receive the error:
`Manager is not accessible via SecurityGroup instance`
Is it possible to achieve this save functionality internal to the SecurityGroup Model, or do I need to create a form and use SecurityGroup.save(commit=False) for this type of pre-save check?
Thanks for the help.
The solution that worked for me was to override the Model's form in admin.py. This enabled a simple check whether admins already existed in a SecurityGroup or not.
from django.contrib import admin
from django.forms import ModelForm
from security.models import SecurityGroup
class SecurityGroupAdminForm(ModelForm):
class Meta:
model = SecurityGroup
fields = '__all__'
def clean(self):
# CHECK 1
if admins:
admins = self.cleaned_data['admins']
for a in admins:
existing_group = SecurityGroup.objects.filter(users__username=a.username)
if existing_group:
raise Exception("message")
return self.cleaned_data
Then, within the same admin.py file, indicate the custom form as part of the admin registration for the model of interest (in this case, SecurityGroup):
class UserSecurityGroupAdmin(admin.ModelAdmin):
# class Meta:
model = UserSecurityGroup
form = UserSecurityGroupAdminForm
admin.site.register(UserSecurityGroup, UserSecurityGroupAdmin)
The error is caused by accessing the Manager of a model through a model instance. You should have used
self.model_class().objects
I'm in the process of debugging my custom permissions class and returning a value of False for my has_object_permission() function, but my I'm still able to access my API (GET request), via Restframework's API browser without authenticating and I can't understand why. Any help would be greatly appreciated. Please see code below. for whatever reasons, it appears that my has_object_permission function is not executing. Please Help
urls.py
router = BulkRouter()
router.register(r'api1', SimpleViewSet1)
urlpatterns = [
url(r'^test/', include(router.urls, namespace='api1')),
]
views.py
class SimpleViewSet1(generics.BulkModelViewSet):
queryset = Barcode.objects.all()
permission_classes = (MyUserPermission,)
serializer_class = SimpleSerializer1
def get_queryset(self):
user = User.objects.get(pk=2)
return Barcode.objects.filter(owner = user)
def get_object(self):
obj = get_object_or_404(self.get_queryset())
self.check_object_permissions(self.request, obj)
return obj
permissions.py
class MyUserPermission(BasePermission):
def has_permission(self, request, view):
return True
def has_object_permission(self, request, view, obj):
return False
serializer.py
class SimpleSerializer1(BulkSerializerMixin, # only required in DRF3
ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
class Meta(object):
model = Barcode
# only required in DRF3
list_serializer_class = BulkListSerializer
fields = ('barcode_number', 'barcode_type', 'owner')
models.py
#python_2_unicode_compatible
class Barcode(models.Model):
owner = models.ForeignKey('auth.User', related_name = 'barcodes')
barcode_number = models.CharField(max_length=200)
barcode_type = models.CharField(max_length=200)
def __str__(self):
return self.barcode_number
Django Rest API Guide says:
Also note that the generic views will only check the object-level permissions for views that retrieve a single model instance. If you require object-level filtering of list views, you'll need to filter the queryset separately. See the filtering documentation for more details.
rest_framework.generics.BulkModelViewSet, as it's name suggests,does bulk operations. It means that you have to use object-level filtering as proposed in the docs.
You should be looking especially under this section. Pay close attention to the example and make use of the code. You should also read about the DjangoModelPermissions to understand how does the example in the link above works.