My Django Rest Framework project have models field where any authenticated users can create model instance.
However I wanted to make sure that only Django Admin can change the accepted field value.
What is the best way to prevent other users from changing the accepted field ?
Pls note that I want to keep permission for authenticated users to create model instance keeping default accepted field.
MODELS.PY
class PO(models.Model):
name = models.CharField(max_length=100)
accepted=models.BooleanField(default=False) # I want this field to be changed only by admin user
VIEWS.PY
class POcreate(generics.CreateAPIView):
queryset = PO.objects.all()
serializer_class = POserializer
permission_classes = [permissions.IsAuthenticated]
SERIALIZER.PY
class POserializer(serializers.ModelSerializer):
class Meta:
model=PO
fields='__all__'
changed view layer and serializer
def get_serializer_class(self):
if self.request.user.is_staff:
return POAdminserializer
return POserializer
class POAdminserializer(serializers.ModelSerializer):
class Meta:
model=PO
fields='__all__'
class POserializer(serializers.ModelSerializer):
class Meta:
model=PO
exclude=('created','accepted','delivered','rejected','rejected_reason')
Related
So basically I have a django model that has a ManyToManyField of friends and two methods that run on it. Here are my files:
Models.py:
from django.db import models
from django.contrib.auth.models import User
class Profile(models.Model):
first_name = models.CharField(max_length=50, blank=True)
last_name = models.CharField(max_length=50, blank=True)
user = models.OneToOneField(User, on_delete=models.CASCADE)
friends = models.ManyToManyField(User, blank=True, related_name='friends')
def friends_list(self):
return self.friends.all()
def number_of_friends(self):
return self.friends.all().count()
Serialzers.py:
from rest_framework import serializers
from .models import Profile
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = '__all__'
Views.py:
from rest_framework import viewsets, permissions
from .models import Profile
from .serializers import ProfileSerializer
class ProfileViewSet(viewsets.ModelViewSet):
queryset = Profile.objects.all()
permission_classes = [
permissions.AllowAny
]
serializer_class = ProfileSerializer
The issue is that in the Api, the return values of the method aren't there. The friends_list method for example is supposed to return a list of friends you have and even though this does work in a traditional django project, the Django Rest Framework is not showing any value for this method. How can I fix this and get the return values for both methods to show up in the api?
Since the model serializer picks up only model fields for the serializer fields, you won't automatically get any methods copied over.
You can still send this read only data over the API by explicitly adding the two fields with reference to the model methods
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = [
# need to explicitly define all fields I believe
'friends_list',
'number_of_friends',
]
Now that the two fields (matching the method name are declared, DRF should create SerializerMethodField or ReadOnly field (not sure which one, but they are similar) for each of them.
It works coz it sets the source for those fields to be the same name, and if finds some attribute (in this case the methods) on the model.
If that doesn't work, you can
class ProfileSerializer(serializers.ModelSerializer):
friends_list = serializers.SerializerMethodField()
number_of_friends = serializers.SerializerMethodField()
class Meta:
model = Profile
fields = [
# need to explicitly define all fields I believe
'friends_list',
'number_of_friends',
]
def get_friends_list(self, instance):
return instance.friends_list()
def get_number_of_friends(self, instance):
return instance.number_of_friends()
when you use __all__ it call fields only you have to call fields with methods using list like that
`fileds = ["first_name","last_name","user",
"friends","friends_list","number_of_friends"
]`
I'm trying to link 'owner' field of my model to an AbstractUser. I need it to be done automatically, the only think i'm able to do by myself is to allow user logged in to choice between every existing user with, what's not what i want. I would like to not have a field to manipulate, but a outcome serializer with id or username of User that added the model. I'm trying to find solutions for a few days, I've tried already combine ForeignKey, PrimaryKeys, OneToOneField, HiddenField, get_user, perform_create, but I'm for sure doing something wrong, and i'm almost lost with it. The last thing i tried is to def_perform in views like DRF QuickStart tutorial say, but without results.
I add some code sample to be more understandable:
There is my AbstractUser model:
from django.db import models
from django.contrib.auth.models import AbstractUser
class UserProfile(AbstractUser):
username = models.CharField(max_length=20, unique=True)
...
i added it to AUTH_USER_MODEL = in the settings.
And there is other model which i want to link with User:
from django.db import models
from users.models.user import UserProfile
class MyPhoto(models.Model):
owner = models.ForeignKey(UserProfile, related_name='photos', on_delete=models.CASCADE, null=True)
image = models.ImageField(upload_to='Images')
serializer.py
class MyPhotoSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
class Meta:
model = MyPhoto
fields = ('pk', 'image', 'owner')
def create(self, validated_data):
photo = MyPhoto.objects.create(
image=validated_data['image']
)
photo.save()
return photo
views.py
class UpdateMyPhotoViewSet(viewsets.ModelViewSet):
queryset = MyPhoto.objects.all()
serializer_class = MyPhotoSerializer
permission_classes = (IsAuthenticated,)
def perform_create(self, serializer):
serializer.save(created_by=self.request.user)
and for now i can't see the owner field results.
Thanks in advance.
I have the following models:
class UserProfile(models.Model):
user = models.OneToOneField(User)
class Property(models.Model):
user = models.ForeignKey(User)
I would like to create a TabularInline displaying every Property connected to a particular UserProfile on its Django admin page. The problem here is, of course, that Property does not have a ForeignKey directly to UserProfile, so I cannot simply write
class PropertyTabularInline(admin.TabularInline):
model = Property
class UserProfileAdmin(admin.ModelAdmin):
inlines = (PropertyTabularInline,)
How can I easily do what I want?
You can overwrite the User admin page to display both the Profile and the Property models.
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from myapp.models import *
class ProfileInline(admin.TabularInline):
model = Profile
class PropertyInline(admin.TabularInline):
model = Property
class UserAdmin(UserAdmin):
inlines = (ProfileInline, PropertyInline,)
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
You can also remove any unwanted/unused User properties from being displayed (e.g. Groups or Permissions)
more here: https://docs.djangoproject.com/en/1.8/topics/auth/customizing/#extending-the-existing-user-model
and here:
https://docs.djangoproject.com/en/1.8/topics/auth/customizing/#a-full-example
class PropertyTabularInline(admin.TabularInline):
model = Property
def formfield_for_dbfield(self, field, **kwargs):
if field.name == 'user':
# implement your method to get userprofile object from request here.
user_profile = self.get_object(kwargs['request'], UserProfile)
kwargs["queryset"] = Property.objects.filter(user=user_profile)
return super(PropertyInLine, self).formfield_for_dbfield(field, **kwargs)
once this is done, you can add this inline to user UserProfileAdmin like:
class UserProfileAdmin(admin.ModelAdmin):
inlines = (PropertyTabularInline,)
Haven't tested it, but that should work.
It is achievable by making one change in your models.
Instead of creating OneToOne relationship from UserProfile to User, subclass User creating UserProfile. Code should look like that:
class UserProfile(User):
# some other fields, no relation to User model
class Property(models.Model):
user = models.ForeignKey(User)
That will result in creating UserProfile model that have hidden OneToOne relation to User model, it won't duplicate user model.
After doing that change, your code will work. There are some changes under the hood, like UserProfile no longer have it's own ID, you can access fields from User inside UserProfile and it's hard to swap User model using settings.AUTH_USER_MODEL (that will require creating some custom function returning proper type and changing migration by hand) but if this is not a problem for you, it may be good solution.
I'm using the Django's default user and a REST API endpoint to list users filtered by query parameters. It looks like this:
# serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
# views.py
class UserFilter(django_filters.FilterSet):
class Meta:
model = User
class UserList(generics.ListAPIView):
model = User
serializer_class = UserSerializer
filter_class = UserFilter
This works good as long as I provide User's field name in query parameter. So, if I want to filter users based on username, I would use /users/?username=johndoe. But what if I want to map a query parameter which is not a field in User model? Let's say, I have a query param called nickname and I want to use it to filter on first_name field?
Using URLConf and named group might be a solution but if I do it for 15 fields then it'll be very long.
What's the better way?
You can add nickname to your FilterSet pointing to actual field on the model.
# views.py
class UserFilter(django_filters.FilterSet):
nickname = django_filters.CharFilter(name='first_name')
class Meta:
model = User
I have a model named Project which has a m2m field users. I have a task model with a FK project. And it has a field assigned_to. How can i limit the choices of assigned_to to only the users of the current project?
You could do this another way, using this nifty form factory trick.
def make_task_form(project):
class _TaskForm(forms.Form):
assigned_to = forms.ModelChoiceField(
queryset=User.objects.filter(user__project=project))
class Meta:
model = Task
return _TaskForm
Then from your view code you can do something like this:
project = Project.objects.get(id=234)
form_class = make_task_form(project)
...
form = form_class(request.POST)
You need to create a custom form for the admin.
Your form should contain a ModelChoiceField in which you can specify a queryset parameter that defines what the available choices are. This form can be a ModelForm.
(the following example assumes users have an FK to your Project model)
forms.py
from django import forms
class TaskForm(forms.ModelForm):
assigned_to = forms.ModelChoiceField(queryset=Users.objects.filter(user__project=project))
class Meta:
model = Task
Then assign the form to the ModelAdmin.
admin.py
from django.contrib import admin
from models import Task
from forms import TaskForm
class TaskAdmin(admin.ModelAdmin):
form = TaskForm
admin.site.register(Task, TaskAdmin)