I am trying to make a field investor required as False in Django Restframework's ModelSerializer.
class WatchListSerializer(serializers.ModelSerializer):
class Meta:
model = WatchList
fields = ['id', 'investor', 'property']
extra_kwargs = {
'investor' : {
'required' : False
}
}
However, both the fields are defined as ForeignKeys in the model. As you can see in above implementation I am trying to override the default implementation of field investor using extra_kwargs. Unfortunately, it still asking to provide the value for investor.
Here is the model Watchlist -
class WatchList(BaseModel):
investor = models.ForeignKey(User, on_delete=models.PROTECT, blank=False, null=True)
property = models.ForeignKey(Properties, on_delete=models.PROTECT, blank=False, null=True)
class Meta:
unique_together = (('investor', 'property',))
def __str__(self):
return f'Watch List Object {self.id}:'
With the blank=… parameter [Django-doc], you specify whether the field is required in a ModelForm, ModelAdmin, ModelSerializer and other parts of the code that process data into a model object.
You thus can disable this with:
class WatchList(BaseModel):
# …
property = models.ForeignKey(
Properties,
on_delete=models.PROTECT,
blank=True,
null=True
)
Related
Looking for solution of this problem I encountered some similar threads, but referring to older versions of Django/DRF and thus not working in my case.
There are these two models:
class CsdModel(models.Model):
model_id = models.CharField("Item ID", max_length=8, primary_key=True)
name = models.CharField("Item Name", max_length=40)
active = models.BooleanField(default=True)
def __str__(self):
return self.model_id
class CsdListing(models.Model):
model_id = models.ForeignKey(CsdModel, on_delete=models.CASCADE, default=0, related_name='m_id')
name = models.ForeignKey(CsdModel, on_delete=models.CASCADE, default=0, related_name='m_name')
(...)
EDIT: Serializers are defined this way:
class CsdModelSerializer(serializers.ModelSerializer):
model_id = serializers.RegexField(regex='^\w{2}\d{3}$', allow_blank=False)
name = serializers.CharField(min_length=6, max_length=50, allow_blank=False)
class Meta:
model = CsdModel
fields = '__all__'
class CsdListingSerializer(serializers.ModelSerializer):
session_id = serializers.RegexField(regex='^s\d{2}$', allow_blank=False)
def validate_session_id(self, value):
(...)
class Meta:
model = CsdListing
fields = '__all__'
What I'd like to see, is model_id and name from CsdModel displayed inside a form created based on CsdListing model. But instead, the ID is duplicated:
How should I rebuild the model(s) to have both ID and name displayed in the form?
You should have only one foreign key. But the listing serializer should then reference the model as a nested serializer.
class CsdListing(models.Model):
model = models.ForeignKey(CsdModel, on_delete=models.CASCADE, default=0, related_name='listing')
class CsdListingSerializer(serializers.ModelSerializer):
model = CsdModelSerializer()
session_id = serializers.RegexField(regex='^s\d{2}$', allow_blank=False)
I have the following model and I would like to add a custom field called choices which will be a list of choices.
class HPIQuestionBank(models.Model):
label = models.CharField(
max_length=200,
db_index=True,
blank=True,
null=True)
template = models.ForeignKey(
HPIFilter, blank=True, null=True, on_delete=models.CASCADE, default='')
I have implemented the following in the serializers.
class CheckBoxesListField(serializers.ListField):
child = serializers.CharField(allow_null = True, allow_blank=True)
class TemplateQuestionBankSerializer(serializers.ModelSerializer):
answer_type = serializers.CharField(allow_null = True, allow_blank=True)
checkboxes = CheckBoxesListField()
hpianswers_set =TemplateAnswerSerializer(many=True)
class Meta:
model = HPIQuestionBank
fields = ['id','label','hpianswers_set','answer_type','checkboxes']
I'm using the serializer on my GET method. When I attempt to make a request I get the following error:
AttributeError at /api/clinic2/history/template/6/
Got AttributeError when attempting to get a value for field `checkboxes` on serializer `TemplateQuestionBankSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `HPIQuestionBank` instance.
Original exception text was: 'HPIQuestionBank' object has no attribute 'checkboxes'.
If you need to read data only you can try:
class Meta:
model = HPIQuestionBank
fields = ['id','label','hpianswers_set','answer_type',]
read_only_fields = ['checkboxes',]
Or you can work with SerializerMethodField as
class TemplateQuestionBankSerializer(serializers.ModelSerializer):
answer_type = serializers.CharField(allow_null = True, allow_blank=True)
checkboxes = SerializerMethodField()
def get_checkboxes(self, instance):
return CheckBoxesListField(instance).data
I have three models, currently i am using an url like so to do updates and get content:
http://localhost:8000/manuscripts-api/manuscriptlibrary/28/
My question relates to the approach i should use so that i can include in my ManuscriptItem model the IDs of the related Libraries and Settings. What kind of fields can i add to the ManuscriptItem model that does this?
My models:
class ManuscriptItem(models.Model):
"""Represents a single manuscript's content"""
author = models.ForeignKey('accounts_api.UserProfile', on_delete=models.CASCADE)
title = models.CharField(max_length=255, blank=True)
content = models.CharField(max_length=99999999, blank=True)
def __str__(self):
"""Django uses when it needs to convert the object to a string"""
return str(self.id)
class ManuscriptLibrary(models.Model):
"""Represents a single manuscript's library"""
manuscript = models.OneToOneField(ManuscriptItem, on_delete=models.CASCADE)
bookmarks = models.CharField(max_length=99999999)
history = models.CharField(max_length=99999999)
def __str__(self):
"""Django uses when it needs to convert the object to a string"""
return str(self.manuscript)
class ManuscriptSettings(models.Model):
"""Represents a single manuscript's settings"""
manuscript = models.OneToOneField(ManuscriptItem, on_delete=models.CASCADE)
citation_suggestions = models.BooleanField(default=False)
terminology_suggestions = models.BooleanField(default=False)
paper_suggestions = models.BooleanField(default=False)
def __str__(self):
"""Django uses when it needs to convert the object to a string"""
return str(self.manuscript)
My serializers:
class ManuscriptItemSerializer(serializers.ModelSerializer):
"""A serializer for manuscript items."""
class Meta:
model = models.ManuscriptItem
fields = ('id', 'author', 'title', 'content')
extra_kwargs = {'author': {'read_only': True}}
class ManuscriptLibrarySerializer(serializers.ModelSerializer):
"""A serializer for a manuscript's library."""
class Meta:
model = models.ManuscriptLibrary
fields = ('id', 'manuscript', 'bookmarks', 'history')
class ManuscriptSettingsSerializer(serializers.ModelSerializer):
"""A serializer for a manuscript's settings."""
class Meta:
model = models.ManuscriptSettings
fields = ('id', 'manuscript', 'citation_suggestions', 'terminology_suggestions', 'paper_suggestions')
You don't necessarily need to add any new fields to the ManuscriptItem model. You can access the id of the related ManuscriptLibrary and ManuscriptSettings objects by defining the related_name property of the foreign key.
class ManuscriptLibrary(models.Model):
manuscript = models.OneToOneField(ManuscriptItem, on_delete=models.CASCADE, related_name='library')
class ManuscriptSettings(models.Model):
manuscript = models.OneToOneField(ManuscriptItem, on_delete=models.CASCADE, related_name='setting')
Once this is migrated, you can use manuscript_item.library to access the related library object, and manuscript_item.setting to access the related setting. Accessing ids can be done via manuscript_item.library.id.
Edit: To display the ids in the serialized object, you can modify your ManuscriptItemSerializer as given below
class ManuscriptItemSerializer(serializers.ModelSerializer):
library = ManuscriptLibrarySerializer(required=False)
setting = ManuscriptSettingsSerializer(required=False)
class Meta:
model = models.ManuscriptItem
fields = ('id', 'author', 'title', 'content', 'library', 'setting', )
by the docs one_to_one
your ManuscriptItem instance has two property manuscriptlibrary -- instance of the ManuscriptLibrary model and manuscriptsettings instance of the ManuscriptSettings model. So you can get the id by manuscriptlibrary.pk and manuscriptsettings.pk, but may be best solution for greater readability you can use related_name as arjun27 write.
I have the following models:
class Category(models.Model):
name = models.CharField()
... # fields omitted
class Prediction(models.Model):
conversation = models.ForeignKey(Conversation)
category = models.ForeignKey(Category)
... # fields omitted
class Conversation(models.Model):
sid = models.CharField()
... # fields omitted
Now I'm trying to create a model serializer for Category that would return me the following serialized object:
{
"name":"blah",
"conversations":[
"2af22188c5c97256", # This is the value of the sid field
"073aef6aad0883f8",
"5d3dc73fc8cf34be",
]
}
Here is what I have in my serializer:
class CategorySerializer(serializers.ModelSerializer):
conversations = serializers.SlugRelatedField(many=True,
read_only=True,
source="prediction_set",
slug_field='conversation.sid')
class Meta:
model = models.Class
fields = ('class_name', 'conversations')
However, this doesn't work because somehow django doesn't allow me to set slug_field to be a field that's within an object field. Any suggestions on how to accomplish this?
You are modelling a Many-to-Many relationship between Categorys and Conversations with a explicit table called Prediction. The django way of doing this would be to explicitly state the Many-to-Many on either side of the relationship and specify Prediction as the "through-model":
Shamelessly stolen example from this question:
class Category(models.Model):
name = models.CharField(max_length=255)
slug = models.SlugField(unique=True, max_length=255, blank=True,default=None)
desc = models.TextField(blank=True, null=True )
...
class Post(models.Model):
title = models.CharField(max_length=255)
pub_date = models.DateTimeField(editable=False,blank=True)
author = models.ForeignKey(User, null=True, blank=True)
categories = models.ManyToManyField(Category, blank=True, through='CatToPost')
...
class CatToPost(models.Model):
post = models.ForeignKey(Post)
category = models.ForeignKey(Category)
...
This shows a good way to set up the relationship.
Equally shamelessly stolen from the answer by #Amir Masnouri:
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ('name','slug')
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ('id','{anything you want}','categories')
depth = 2
This shows a nice way of achieving the nested serialization behavior that you would like.
Is it possible to create a verbose name for the actual Class model?
class User(models.Model):
fname = models.CharField(max_length=50, verbose_name = 'first name')
So in the admin panel it will be referenced by its verbose name and not 'user' ?
class User(models.Model):
fname = models.CharField(max_length=50, verbose_name = 'first name')
class Meta:
verbose_name = "users"
Source: https://docs.djangoproject.com/en/2.1/topics/db/models/#meta-options
verbose_name and verbose_name_plural both the properties of Meta class are very important to modify the default behaviour of Django to display the name of our models on Admin interface.
You can change the display of your model names using on Admin Interface using verbose_name and verbose_name_plural properties and model fields names using keyword argument verbose_name.
Please find the below 2 examples.
Country model:
class Country(models.Model):
name = models.CharField(max_length=100, null=False, blank=False, help_text="Your country", verbose_name="name")
userid = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return f"Country {str(self.id)} - {self.name}"
class Meta:
verbose_name = "Country"
verbose_name_plural = "Countries"
If you will not specify verbose_name_plural then Django will take it as Countrys which does not look good as we want it as Countries.
This better fits in the following type of Model.
Gender model:
class Gender(models.Model):
name = models.CharField(max_length=100, null=False, blank=False, help_text="Gender", verbose_name = "name")
userid = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return f"Gender {str(self.id)} - {self.name}"
class Meta:
verbose_name = "Gender"
You could add a "verbose_name_plural" to the "Meta" class too.
To alter admin model without polluting the model itself, you can utilize a proxy admin model, like this:
# admin.py
from . import models
class Users(models.User):
class Meta:
proxy = True
class UsersAdmin(admin.ModelAdmin):
...
admin.site.register(Users, UsersAdmin)
ConfigAbility._meta.verbose_name = 'config ability'
ConfigAbility._meta.verbose_name_plural = 'config ability'
I did explore this, but don't know whether it's the thing you need. I put those codes in class ConfigAbilityAdmin in Admin.py. Then, the result:
enter image description here
With this approach, you don't need to config Meta method in model class, especially when model class's code is generated from inspectdb...