Django rest framework auto-populate filed with user.id - python

I cant find a way to auto-populate the field owner of my model.I am using the DRF .If i use ForeignKey the user can choose the owner from a drop down box , but there is no point in that.PLZ HELP i cant make it work.The views.py is not include cause i think there is nothing to do with it.
models.py
class Note(models.Model):
title = models.CharField(max_length=200)
body = models.TextField()
cr_date = models.DateTimeField(auto_now_add=True)
owner = models.CharField(max_length=100)
# also tried:
# owner = models.ForeignKey(User, related_name='entries')
class Meta:
ordering = ('-cr_date',)
def __unicode__(self):
return self.title
serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', "username", 'first_name', 'last_name', )
class NoteSerializer(serializers.ModelSerializer):
owner = request.user.id <--- wrong , but is what a need.
# also tried :
# owner = UserSerializer(required=True)
class Meta:
model = Note
fields = ('title', 'body' )

Django Rest Framework provides a pre_save() method (in generic views & mixins) which you can override.
class NoteSerializer(serializers.ModelSerializer):
owner = serializers.Field(source='owner.username') # Make sure owner is associated with the User model in your models.py
Then something like this in your view class:
def pre_save(self, obj):
obj.owner = self.request.user
REFERENCES
http://www.django-rest-framework.org/tutorial/4-authentication-and-permissions#associating-snippets-with-users
https://github.com/tomchristie/django-rest-framework/issues/409#issuecomment-10428031

Related

Django Form: Only show manytomany objects from logged in user

The current problem is that my form shows the logged in user all Portfolios ever created. The form should only show portfolios that the logged-in user created.
Something like this:
associated_portfolios manytomany field = ...objects.filter(user=user_id)
I'm not sure if this should be implemented in the forms.py or views.py and if so how. I've been going through the django documentation and found 'formfield_for_manytomany' but not sure if this is only meant for admin.
Models.py
class Portfolio(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=20)
description = models.CharField(max_length=250, blank=True, null=True)
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=50)
body = RichTextUploadingField(blank=True, null=True)
associated_portfolios = models.ManyToManyField(Portfolio, blank=True)
created_on = models.DateField(auto_now_add=True, editable=False)
Views.py
class PostCreate(CreateView):
model = Post
form_class = PostCreateForm
def formfield_for_manytomany(self, db_field, request, **kwargs):
self.fields['associated_portfolios'] = Portfolio.objects.filter(user=self.request.user)
return super().formfield_for_manytomany(db_field, request, using=self.using, **kwargs)
forms.py
class PortfolioCreateForm(ModelForm):
class Meta:
model = Portfolio
fields = ['user', 'name', 'description']
class PostCreateForm(ModelForm):
class Meta:
model = Post
fields = ['user', 'title', 'body', 'category', 'associated_portfolios']
Since you're using a ModelForm, the associated_protfolios field will be a ModelMultipleChoiceField [docs]. This field has a queryset attribute [docs]. We want to modify that attribute.
Django's CreateView has a method get_form, which in this case will grab your PostCreateForm. This is a good spot to filter the field's queryset, since we have access to the user:
class PostCreate(CreateView):
model = Post
form_class = PostCreateForm
def get_form(self, *args, **kwargs):
form = super().get_form(*args, **kwargs) # Get the form as usual
user = self.request.user
form.fileds['associated_portfolios'].queryset = Portfolio.objects.filter(user=user)
return form
Did you try this
self.fields['associated_portfolios'] = Post.objects.filter(associated_portfolios__portfolio__user=request.user)
OR
user_posts = Post.objects.filter(user=request.user)
self.fields['associated_portfolios'] = user_posts.associated_portfolios.all()
read more about M2M relationships querying here, because I think your problem may be with it.
Also, I'm not sure about your actual data maybe it's right and it gives a correct result as filtering Portfolio model against current user to get its objects looks right for me, but anyway double check everything again.
And as a final note, add related_name to your model fields so you can use it easily for reverse relations rather than going with Django's default naming, it will be clearer and give a better understanding.

the model field's form disappears in django admin

I have two models, which are User and Record. Each has several fields.
from django.db import models
class User(models.Model):
openid = models.CharField(max_length=20)
nickname = models.CharField(max_length=20,null=True)
def __str__(self):
return self.nickname
class Record(models.Model):
expression = models.CharField(max_length=100)
user = models.ForeignKey(User)
time = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.expression
I register them in admin.py
from django.contrib import admin
from .models import User,Record
class RecordAdmin(admin.ModelAdmin):
list_display = ('expression','user','time')
class UserAdmin(admin.ModelAdmin):
empty_value_display = "çİş"
list_display = ('openid','nickname')
admin.site.register(User,UserAdmin)
admin.site.register(Record,RecordAdmin)
it works well in django admin initially. but one day, the fields of the Record model disppeared. It looks like
.
No field displays. It makes me unable to modify or add the values of the Record model. The other model User works well and all data exists in database. So why?
I think you just have to add on_delete=models.CASCADE in your ForeignKey Field. When you are using this kind of field, you have to specify the comportment when you make an update, a delete or anything else on this field.
So your script should be like this :
class Record(models.Model):
expression = models.CharField(max_length=100)
user = models.ForeignKey(User, on_delete=models.CASCADE)
time = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.expression
This is the result :
Edit :
You can also modify null=True by default=null
class User(models.Model):
openid = models.CharField(max_length=20)
nickname = models.CharField(max_length=20,default=null)
def __str__(self):
return self.nickname

How can I filter a ManyToManyField against the current User in the Browsable API in DRF?

I have 2 models, Todo and a Tag. Todo has a ManyToMany relationship with Tag. When adding new Todos from the Browsable API, I want to be able to see only the Tags added by the current user as the available options in the multiselect. Currently, it shows all the added Tags, irrespective of who added them. I want to limit the options to only show the Tags added by the current user. (Authentication is setup already)
The models:
class Todo(models.Model):
title = models.CharField(max_length=100)
description = models.TextField(blank=True)
created_at = models.DateTimeField(auto_now_add=True)
due_at = models.DateTimeField(blank=True)
updated_at = models.DateTimeField(auto_now=True)
tags = models.ManyToManyField(Tag, related_name='todos')
creator = models.ForeignKey(User, on_delete=models.CASCADE, related_name='todos')
class Tag(models.Model):
name = models.CharField(max_length=20)
creator = models.ForeignKey(User, on_delete=models.CASCADE, related_name='created_tags')
def __str__(self):
return self.name
The Serializer:
class TodoCreateSerializer(serializers.ModelSerializer): #This is the one being used for a POST
class Meta:
model = models.Todo
fields = ('title', 'description', 'due_at', 'tags')
Is there some serializer field or some other way to specify which queryset to use in the Serializer? Is there another better approach?
In your TodoCreateSerializer you need to add PrimaryKeyRelatedField with a custom queryset that has the filtered tags of a user.
First, you will need to create a custom PrimaryKeyRelatedField that filter any objects to get only those who owned by the user.
class UserFilteredPrimaryKeyRelatedField(serializers.PrimaryKeyRelatedField):
def get_queryset(self):
request = self.context.get('request', None)
queryset = super(UserFilteredPrimaryKeyRelatedField, self).get_queryset()
if not request or not queryset:
return None
return queryset.filter(user=request.user)
(This is a generic one and can be used when filtering in objects by user)
Then you should use this one in you TodoCreateSerializer:
class TodoCreateSerializer(serializers.ModelSerializer):
tags = UserFilteredPrimaryKeyRelatedField(queryset= Tag.objects, many=True)
class Meta:
model = models.Todo
fields = ('title', 'description', 'due_at', 'tags')

Django Rest Framework slug_field error

I have this serializer and model. I want to show in my API the field username of User model, but I receive this error.
AttributeError at /api/v1/client_share_requests/1/
'Profile' object has no attribute 'username'
serializers.py
class ClientShareRequestSerializer(serializers.ModelSerializer):
checked_by = serializers.SlugRelatedField(
many=True,
queryset=Profile.objects.all(),
slug_field='username'
)
class Meta:
model = ClientShareRequest
fields = ('checked_by')
models.py
class Profile(models.Model):
user = models.OneToOneField(User, related_name='profile')
class ClientShareRequest(models.Model):
checked_by = models.ManyToManyField(Profile, blank=True,
related_name='checked_by')
I try to access the User model instance like this:
checked_by = serializers.SlugRelatedField(
many=True,
queryset=Profile.objects.all(),
slug_field='user.username'
)
but I receive this error:
'Profile' object has no attribute 'user.username'
Thanks for helping. (Sorry for my English :P)
My guess is DRF does not allow nested attribute lookups on SlugRelatedFields. Simple workaround would be to add a username property on the Profile model and use this in the serializer:
class Profile(models.Model):
user = models.OneToOneField(User, related_name='profile')
#property
def username(self):
return self.user.username
class ClientShareRequest(models.Model):
checked_by = models.ManyToManyField(Profile, blank=True,
related_name='checked_by')
class ClientShareRequestSerializer(serializers.ModelSerializer):
checked_by = serializers.SlugRelatedField(
many=True,
read_only=True,
slug_field='username'
)
class Meta:
model = ClientShareRequest
fields = ('checked_by', )
This works for reads :)

Django REST Framework: change field names

My Django application provides readonly api access to the users of the site. I created a user profile model and use it in the serializer of the user model:
Model:
# + standard User Model
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
display_name = models.CharField(max_length=20, blank=True)
Serializer:
class UserProfileSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = UserProfile
fields = ('display_name',)
class UserSerializer(serializers.HyperlinkedModelSerializer):
userprofile_set = UserProfileSerializer(many=False, label='userprofile')
class Meta:
model = User
fields = ('id', 'username', 'userprofile_set')
This works but the field userprofile_set looks ugly. Is it possible to change the field name?
To complement your answer, you can also make use of relationships' related names:
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True, related_name='profiles')
display_name = models.CharField(max_length=20, blank=True)
that way you can also use this in your code:
user = User.objects.last() #some user
user.profiles.all() #UserProfiles related to this user, in a queryset
user.profiles.last() #the last UserProfile instance related to this user.
May I recommend that you turn that ForeignKey into a OneToOneField? that way an user can have one and just one user profile, and you don't need to establish uniqueness:
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='profile')
display_name = models.CharField(max_length=20, blank=True)
Oh, I can name the variable userprofile_set as I like. First I tested the name userprofile which conflicted. If I name the field profile it works. :)
class UserSerializer(serializers.HyperlinkedModelSerializer):
profile = UserProfileSerializer(many=False, label='userprofile')
class Meta:
model = User
fields = ('id', 'username', 'profile')

Categories