Specify django-rest-framework field for serializer after request? - django

I've got the following code:
models.py
class Interview(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, help_text="The Primary Key of the Interview")
organization = models.ForeignKey(Organization, on_delete=models.CASCADE, help_text="The Primary Key of the Organization in the interview")
question = name = models.CharField(max_length=200, help_text="The Question")
serializers.py
class InterviewSerializer(serializers.ModelSerializer):
class Meta:
model = Interview
fields = ('organization', 'question')
views.py
class InterviewViewset(viewsets.ModelViewSet):
"""
API endpoint that allows Interviews to be viewed or edited.
"""
serializer_class = InterviewSerializer
Right now, the request body to create an interview requires these fields:
{
"organization": "string",
"question": "string"
}
I'd like the organization for the Interview to automatically be set to the current users organization (request.user.Organization - I've got a custom User model). How can I do this elegantly for my viewset?

One way (probably the best) is overriding save / create / update methods of your ModelSerializer class. DRF docs about it
There you can use self.context.get("request") to get current user. And if you still need organization field to be serialized, just make it read_only=True

Related

Django subscriber feature

I am creating a django site/platform where the main concept is users can create shops and other users can subscribe to those who have shops open (think Etsy). Trying to implement the subscriber feature and this is a model I have so far for it:
class Subscriber(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
sub_shop = models.ForeignKey(Shop, on_delete=models.CASCADE)
It works perfect for giving users the ability to subscribe and have the subscribtions listed in their profile and vice versa for shop owners, but for now a user can subscribe to a shop as many times as they want and I would like to prevent this. Idk if there is a constraint to allow multiple subscriber model instances by the same user but not allow for the same exact 'user' and 'sub_shop' instance OR if I am just going on about this in a very bad way!
You can use a UniqueConstraint [Django-doc] to specify that the combination of user and sub_shop should be unique:
class Subscriber(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
sub_shop = models.ForeignKey(Shop, on_delete=models.CASCADE)
class Meta:
constraints = [
models.UniqueConstraint(
fields=['user', 'sub_shop'],
name='subscribe_once'
)
]
prior to django-2.2, you can work with unique_together [Django-doc]:
# prior to Django-2.2
class Subscriber(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
sub_shop = models.ForeignKey(Shop, on_delete=models.CASCADE)
class Meta:
unique_together = [['user', 'sub_shop']]
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.

How to save a model in Django Rest Framework having one to one relationship

I have a Django model named BankDetail that has a one to one relationship with User.
#BankeDetails
class BankDetail(models.Model):
account_holder_name = models.CharField(max_length=100)
account_number = models.CharField(max_length=50, blank=True, null=True)
iban = models.CharField("IBAN", max_length=34, blank=True, null=True)
bank_name = models.CharField(max_length=100)
bank_address = models.CharField(max_length=500)
swift_bic_code = models.CharField(max_length=11)
user = models.OneToOneField(MyUser,on_delete=models.CASCADE)
accepting_fiat_currency = models.OneToOneField(AcceptedFiatCurrency)
created_at = models.DateTimeField(auto_now_add=True,null=True)
updated_at = models.DateTimeField(auto_now=True)
The serializer is as listed below :
class BankDetailSerializer(serializers.ModelSerializer):
"""Serializer for BankDetails"""
class Meta:
model = BankDetail
fields = "__all__"
def validate(self, data):
if data['account_number'] or data['iban']:
raise serializers.ValidationError("Please fill Account Number or IBAN")
return data
Request Payload :
{
"account_holder_name":"Aladin",
"account_number":"1239893",
"bank_name":"Aladin Bank",
"bank_address":"Republic of Wadia",
"swift_bic_code":"1",
"user_id":"1",
"accepting_fiat_currency_id":"1"
}
Now when I'm trying to save the model from my view, I get the following error :
{
"user": [
"This field is required."
],
"accepting_fiat_currency": [
"This field is required."
]
}
How can I pass I refrence ob these objects, do I need to manually retrieve them from the db using the id's?
AFAIR you should define a related model field in the serializer. And don't use __all__ instead of explicit write all fields. It's my recommendation :)
You should find answer in following questions:
Post from StackOverflow
That helped me last week
DRF documentation about Serializer relations
One solution is to use different serializer for creating/retrieving data in/from database.
Your serializer for creating should be something like -
class BankDetailSerializer(serializers.ModelSerializer):
"""Serializer for BankDetails"""
class Meta:
model = BankDetail
exclude = ('user', 'accepting_fiat_currency', ) # Note we have excluded the related fields
def validate(self, data):
if data['account_number'] or data['iban']:
raise serializers.ValidationError("Please fill Account Number or IBAN")
return data
Then while saving the serializer pass the above two excluded fields. Like -
serializer = BankDetailSerializer(data=request.data)
if serializer.is_valid():
serializer.save(user=request.user, accepting_fiat_currency=<AcceptedFiatCurrency-object>)
If you are using Generic Views in DRF then I would suggest you to look
into perform_create method.
You need to send "user":"1". instead of "user_id":"1". The same with other "accepting_fiat_currency"

serialize verbose fields of related foreign key object in django REST?

for an app with three objects - User, Event, and Action where users take actions on events creating an object like:
class Action(models.Model):
event = models.ForeignKey('CauseActivity', on_delete=models.CASCADE, related_name='actions')
user = models.ForeignKey('users.User', on_delete=models.CASCADE, related_name='actions')
type = models.CharField(max_length=100, choices=ACTION_TYPES)
date_created = models.DateTimeField(auto_now_add=True)
date_updated = models.DateTimeField(auto_now=True)
I would like our RESTful API to be able to return the actions for one specific user, either within the user request (which requires no extra API calls) or with one additional request (get actions by user)
a simple User serializer with 'actions' as a field will return only the obj ids (response to GET ex: "actions":[108,109])
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = (
..., 'actions', )
I am aware of nesting to serialize custom fields like username = serializers.ReadOnlyField(source='user.name') but is there a way to use serializers.ListSerializer or .ModelSerializer to serialize the full set of Action objects' fields within my UserSerializer? I read through https://www.django-rest-framework.org/api-guide/serializers/ and best approach isn't really clear to me. thanks
create a serializer for your Action model (let's call it ActionSerializer) then make your code like
class UserSerializer(serializers.ModelSerializer):
actions = ActionSerializer(many=True)
class Meta:
model = User
fields = ('actions',...)

Django Rest API, how to create post api for 2 model entries, and has foreign key associated to the model

I am trying to use DRF Django Rest Framework to create a post API to create entry for 2 models and associate foreign key relationship. How do I accomplish that ?
I have 2 models
- Employee model that OneToOne association with User, and has a ForeignKey Company
- Company model
I want to have a post to create employee model entry and also company model entry and associate the employee to the company. The employee also I want to enter in the User data (username, first_name, last_name, etc).
The following are the code excerpts:
https://gitlab.com/firdausmah/railercom/blob/master/railercomapp/models.py
class Employee(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='employee')
company = models.ForeignKey(Company)
class Company(models.Model):
name = models.CharField(max_length=50)
tel = models.CharField(max_length=15, blank=True)
https://gitlab.com/firdausmah/railercom/blob/master/railercomapp/views.py
class EmployeeWithCompanyCreateView(generics.ListCreateAPIView):
"""This class defines the create behavior of our rest api."""
queryset = Employee.objects.all()
serializer_class = EmployeeWithCompanyCreateSerializer
def perform_create(self, serializer):
"""Save the post data when creating a new bucketlist."""
serializer.save()
https://gitlab.com/firdausmah/railercom/blob/master/railercom/urls.py
urlpatterns = [
url(r'^employee/$', EmployeeWithCompanyCreateView.as_view(), name="create"),
https://gitlab.com/firdausmah/railercom/blob/master/railercomapp/serializers.py
class EmployeeWithCompanyCreateSerializer(serializers.ModelSerializer):
class Meta:
model = Employee
fields = ("id","identity_number", "tel")
Your current solution has some thing wrong: the fields list of EmployeeWithCompanyCreateSerializer class is mismatch the fields of Employee class.
In your question context ,I suggest you write the complicated view by hand.

Django REST Framework: how to make Id field required upon POST, i.e. non read-only field?

I've got a model, where I've overridden id as a CharField and primary key. Here's the model and its serializer:
class Tool(models.Model):
id = models.CharField(max_length=10000, primary_key=True, default=uuid.uuid4, editable=False)
description = models.TextField(null=True, blank=True)
...
class ToolSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Tool
fields = (
'id',
'description',
...
)
By default, Django REST Framework marks id field as read-only and doesn't require it upon POST requests. But I want it to be writable and require it upon POST. How do I achieve that?
I think, I found the answer in an unexpected place of DRF documentation:
http://www.django-rest-framework.org/api-guide/serializers/#customizing-multiple-update
I need to create an explicit id field in serializer like this:
class ToolSerializer(serializers.HyperlinkedModelSerializer):
id = serializers.CharField()
class Meta:
model = Tool
fields = (
'id',
'description',
...
)
This will override the default id field, created as a read-only.