After the form is validated the field is populated with object representation from memory.
Form screenshot
I am using MoneyField in my Product model from django-money library.
models.py
class Product(models.Model):
CURRENCY_CHOICES = (
('EUR', 'EUR'),
('USD', 'USD'),
('GBP', 'GBP'),
('PLN', 'PLN')
)
price = MoneyField(max_digits=10, decimal_places=2, blank=False,
currency_choices=CURRENCY_CHOICES)
serializers.py
class ProductSerializer(serializers.ModelSerializer):
price_currency = serializers.ChoiceField(choices=Product.CURRENCY_CHOICES)
class Meta:
model = Product
fields = (
'price','price_currency',
)
views.py
class ProductList(generics.ListCreateAPIView):
serializer_class = ProductSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
queryset = Product.objects.all()
I would really appreciate any suggestions on why this is happening.
I have the following code working perfectly. I can create a Post object from DRF panel by selecting an image and a user. However I want DRF to populate the user field by the currently logged in user.
models.py
class Post(TimeStamped):
user = models.ForeignKey(User)
photo = models.ImageField(upload_to='upload/')
hidden = models.BooleanField(default=False)
upvotes = models.PositiveIntegerField(default=0)
downvotes = models.PositiveIntegerField(default=0)
comments = models.PositiveIntegerField(default=0)
serializers.py
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['id', 'user', 'photo']
views.py
class PhotoListAPIView(generics.ListCreateAPIView):
queryset = Post.objects.filter(hidden=False)
serializer_class = PostSerializer
authentication_classes = (SessionAuthentication, BasicAuthentication)
permission_classes = (IsAuthenticated,)
How can I do this?
Off the top of my head, you can just override the perform_create() method:
class PhotoListAPIView(generics.ListCreateAPIView):
...
def perform_create(self, serializer):
serializer.save(user=self.request.user)
Give that a shot and let me know if it works
You can use CurrentUserDefault:
user = serializers.PrimaryKeyRelatedField(
read_only=True,
default=serializers.CurrentUserDefault()
)
It depends on your use case. If you want it to be "write-only", meaning DRF automatically populates the field on write and doesn't return the User on read, the most straight-forward implementation according to the docs would be with a HiddenField:
class PhotoListAPIView(generics.ListCreateAPIView):
user = serializers.HiddenField(
default=serializers.CurrentUserDefault(),
)
If you want want it to be readable, you could use a PrimaryKeyRelatedField while being careful that your serializer pre-populates the field on write - otherwise a user could set the user field pointing to some other random User.
class PhotoListAPIView(generics.ListCreateAPIView):
user = serializers.PrimaryKeyRelatedField(
# set it to read_only as we're handling the writing part ourselves
read_only=True,
)
def perform_create(self, serializer):
serializer.save(user=self.request.user)
Finally, note that if you're using the more verbose APIView instead of generics.ListCreateAPIView, you have to overwrite create instead of perform_create like so:
class PhotoListAPIView(generics.ListCreateAPIView):
user = serializers.PrimaryKeyRelatedField(
read_only=True,
)
def create(self, validated_data):
# add the current User to the validated_data dict and call
# the super method which basically only creates a model
# instance with that data
validated_data['user'] = self.request.user
return super(PhotoListAPIView, self).create(validated_data)
You can avoid passing the user in your request and you won't see it in the output but DRF will populate it automatically:
from rest_framework import serializers
class MyModelSerializer(serializers.ModelSerializer):
user = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model = models.MyModel
fields = (
'user',
'other',
'fields',
)
As of DRF version 3.8.0 (Pull Request discussion), you can override save() in serializer.
from rest_framework import serializers
...
class PostSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())
class Meta:
model = Post
fields = ['id', 'user', 'photo']
def save(self, **kwargs):
"""Include default for read_only `user` field"""
kwargs["user"] = self.fields["user"].get_default()
return super().save(**kwargs)
#DaveBensonPhillips's answer might work in your particular case for some time, but it is not very generic since it breaks OOP inheritance chain.
ListCreateAPIView inherits from CreateModelMixin which saves the serializer already. You should always strive to get the full chain of overridden methods executed unless you have a very good reason not to. This way your code stays DRY and robust against changes:
class PhotoListAPIView(generics.ListCreateAPIView):
...
def perform_create(self, serializer):
serializer.validated_data['user'] = self.request.user
return super(PhotoListAPIView, self).perform_create(serializer)
You will have to override the default behavior of how generics.ListCreateAPIView creates an object.
class PhotoListAPIView(generics.ListCreateAPIView):
queryset = Post.objects.filter(hidden=False)
authentication_classes = (SessionAuthentication, BasicAuthentication)
permission_classes = (IsAuthenticated,)
def get_serializer_class(self):
if self.request.method == 'POST':
return CreatePostSerializer
else:
return ListPostSerializer
def create(self, request, *args, **kwargs):
# Copy parsed content from HTTP request
data = request.data.copy()
# Add id of currently logged user
data['user'] = request.user.id
# Default behavior but pass our modified data instead
serializer = self.get_serializer(data=data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
The .get_serializer_class() is not necessary as you can specify which fields are read-only from your serializer, but based on the projects I have worked on, I usually end up with 'asymmetric' serializers, i.e. different serializers depending on the intended operation.
Try this:
def post(self, request, format=None)
serializer = ProjectSerializer(data=request.data)
request.data['user'] = request.user.id
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST
This is what works for me in serializers.py, where I am also using nested data. I want to display created_by_username without having to lookup other users.
class ListSerializer(serializers.ModelSerializer):
"""
A list may be created with items
"""
items = ItemSerializer(many=True)
# automatically set created_by_id as the current user's id
created_by_id = serializers.PrimaryKeyRelatedField(
read_only=True,
)
created_by_username = serializers.PrimaryKeyRelatedField(
read_only=True
)
class Meta:
model = List
fields = ('id', 'name', 'description', 'is_public',
'slug', 'created_by_id', 'created_by_username', 'created_at',
'modified_by', 'modified_at', 'items')
def create(self, validated_data):
items_data = validated_data.pop('items', None)
validated_data['created_by_id'] = self.context['request'].user
validated_data['created_by_username'] = self.context['request'].user.username
newlist = List.objects.create(**validated_data)
for item_data in items_data:
Item.objects.create(list=newlist, **item_data)
return newlist
I wrote an extension to DRF's serializer below
from rest_framework import serializers
class AuditorBaseSerializer(serializers.Serializer):
created_by = serializers.StringRelatedField(default=serializers.CurrentUserDefault(), read_only=True)
updated_by = serializers.StringRelatedField(default=serializers.CurrentUserDefault(), read_only=True)
def save(self, **kwargs):
# if creating record.
if self.instance is None:
kwargs["created_by"] = self.fields["created_by"].get_default()
kwargs["updated_by"] = self.fields["updated_by"].get_default()
return super().save(**kwargs)
and it can be used as follows
class YourSerializer(serializers.ModelSerializer, AuditorBaseSerializer):
class Meta:
model = SelfEmployedBusiness
fields = (
'created_by',
'updated_by',
)
I have a few Django models that I display in the admin using wagtail’s modelAdmin. few of the fields in the models are referencing the user model. Since I’m not creating the form/page and it’s handled by wagtail, how do I pass the current user object to the field when it’s saved? That is, the created and updated by fields should have the current user object assigned to them.
See code snippet below, currently I'm assigning the user manually by querying, instead I'd like to get the current user.
from django.db import models
from django.conf import settings
from django.contrib.auth import get_user_model
from wagtail.admin.forms import WagtailAdminPageForm
STATUS_CHOICES = (
(1, 'Active'),
(2, 'Inactive')
)
class BasePageForm(WagtailAdminPageForm):
def save(self, commit=True):
auth_user_model = get_user_model()
default_user = auth_user_model.objects.get(username='admin')
page = super().save(commit=False)
if not page.pk:
page.created_by = default_user
page.updated_by = default_user
else:
page.updated_by = default_user
if commit:
page.save()
return page
class BaseModel(models.Model):
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='created%(app_label)s_%(class)s_related'
)
created_at = models.DateTimeField(auto_now_add=True)
updated_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='updated%(app_label)s_%(class)s_related'
)
updated_at = models.DateTimeField(auto_now=True)
status = models.IntegerField(choices=STATUS_CHOICES, default=1)
class Meta:
abstract = True # Set this model as Abstract
base_form_class = BasePageForm
I have 3 models. User, process, processmapping. process model has a created_by filed which is a foreign key to user model. processmapping model has processname filed which is a foreign key to process table.
My code is as follows.
models.py
class process(models.Model):
created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_column= "username")
process_name = models.CharField(max_length=200)
....
class processmap(models.Model):
process = models.ForeignKey(process, on_delete=models.CASCADE, db_column="process_name")
....
views.py
class processViewSet(viewsets.ModelViewSet):
permission_classes = (IsAuthenticated,)
queryset= process.objects.all()
serializer_class = processSerializer
filter_backends = (DjangoFilterBackend,filters.SearchFilter)
filter_fields = ('created_by', 'process_name',)
def get_queryset(self):
if self.request.user.is_superuser:
return self.queryset
else:
queryset = self.queryset
query_set = queryset.filter(created_by=self.request.user)
return query_set
class processmapViewset(viewsets.ModelViewSet):
permission_classes = (IsAuthenticated,)
try:
queryset= processmap.objects.all()
serializer_class = processmapSerializer
filter_backends = (DjangoFilterBackend,filters.SearchFilter)
filter_fields = ('process', 'service', 'sequence','process__created_by')
Serializers.py
class processSerializer(serializers.HyperlinkedModelSerializer):
#created_by = UserSerializer(many=True)
class Meta:
model = process
fields = ("__all__")
def perform_create(self, serializer):
serializer.save(user=self.request.user)
class processmapSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = processmap
fields = ("__all__")
def perform_create(self, serializer):
serializer.save(user=self.request.user)
When a user creates a process and then goes to mapping the process, the foreign key lists all the processes that are present in the process table. Instead, I need to list down only the processes that are created by the user logged in. How can I achieve this.
Note: I have searched for the same and most answers involve forms. Please note I'm not using any forms and I want that logic to be implemented in the views.py.
Thanks in advance :)
every one ,,I have
models.py
......
class Place(models.Model):
name = models.CharField(max_length=100, blank=True, null=True)
release = models.DateTimeField(blank=True, null=True)
def __unicode__(self):
return self.name
class ProductsTbl(models.Model):
......
place = models.ManyToManyField(Place)
......
def __unicode__(self):
return self.name
and I have forms.py here
forms.py
from django.forms import ModelForm
from .models import *
class ProductsTblForm(ModelForm):
class Meta:
model = ProductsTbl
fields = ('place',)
......
however,,I need let my templates shows the 'release' in form,,right now,,it only shows the 'name' which under the Class place(models.Model): but no 'release',,how can I let the 'release' can show up?
You can access model instance variables in the form by using form.instance.<field_name>.