I'm trying to extend Django's Poll app. Each poll will have 2 choices and either an image for each choice or a color for each choice, but not both. My models look like this:
class Question(models.Model):
question = models.CharField()
created_at = models.DateTimeField(auto_now_add=True)
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice = models.CharField(max_length=120)
vote_count = models.IntegerField(default=0)
class ChoiceImage(models.Model):
choice = models.OneToOneField(Choice, on_delete=models.CASCADE)
img = models.ImageField()
class ChoiceColor(models.Model):
choice = models.OneToOneField(Choice, on_delete=models.CASCADE)
color = models.CharField(max_length=7)
I have a View that looks like this:
class CreateQuestionView(CreateAPIView):
serializer_class = CreateQuestionSerializer
With the following serializers:
class ChoiceImageSerializer(serializers.ModelSerializer):
class Meta:
model = ChoiceImage
fields = ('image',)
class ChoiceColorSerializer(serializers.ModelSerializer):
class Meta:
model = ChoiceColor
fields = ('color',)
class ChoiceSerializer(serializers.ModelSerializer):
choice_image = ChoiceImageSerializer(many=False)
choice_color = ChoiceColorSerializer(many=False)
class Meta:
model = Choice
fields = ('choice', 'choice_image', 'choice_color')
class CreateQuestionSerializer(serializers.ModelSerializer):
choices = ChoiceSerializer(many=True)
class Meta:
model = Question
fields = ('question', 'choices')
How do I create this constraint on my serializers? Also Am I designing this efficiently or is there a better way to do this?
Related
models.py
class Product(models.Model):
title = models.CharField(max_length=200)
description = models.TextField()
price = models.DecimalField(decimal_places=5,max_digits= 1500)
summary = models.TextField()
featured = models.BooleanField()
def __str__(self):
return self.title
# return f'product title:{self.title}-product price:{self.price}'workok
class Meta:
ordering = ('-price',)
class Opinion(models.Model):
name = models.CharField(max_length=20)
email = models.EmailField(max_length=20)
body = models.TextField()
opinion_date = models.DateTimeField(auto_now_add=True)
active = models.BooleanField(default=False)
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='opinion_set')
def __str__(self):
return f'({self.name}) add opinion about ({self.product})'
forms.py:
from django.forms import ModelForm
from .models import Product #space after from keyword
class OpinionModelForm(ModelForm):
class Meta:
model = Product
fields = ['name','email','body','product']
invalid in code line :
fields = ['name','email','body','product'] #---- NOT WORK !!!
, but if i change above code to :
fields = "__all__" # ----it is WORKing ok without any problem !!
question : what is the error? I am not need all the fields in the Product model (like active boolean field), I need only 'name','email','body','product' fields .
According to the error and the code you provided the main problem is that you made a mistake in chosing model in serializer:
class OpinionModelForm(ModelForm):
class Meta:
model = Product
fields = ['name','email','body','product']
Serializer name is OpinionModelForm and listed fields belong to Opinion so I guess you actually wanted to serialize Opinion and no Product as you defined at this line:
model = Product
Simply change it to:
model = Opinion
My serializers are not showing related Models. I mean the models that have got many-to-one relationship. Please see the code below. Forgive my English.
Completely confusing me please help. I am new to django. I am trying to save my Draft js ContentState to the database. I have made the Post model a Foreignkey to my Block models. But When I try to retrieve the data using django-rest-framework serializers the blocks are not displaying.
Same applies with the Blocks I tried to serialize them on their own but the inlineStyleRanges and entityRanges data is not coming up.
#models.py
class Post(models.Model):
created_by= models.ForeignKey(User, on_delete=models.CASCADE)
cat= models.ForeignKey(Category, on_delete=models.CASCADE)
class Block(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE)
key = models.CharField(max_length=255)
text = models.TextField()
type = models.CharField(max_length=90)
depth = models.IntegerField(default=0)
class InlineStyleRange(models.Model):
block = models.ForeignKey(Block, on_delete=models.CASCADE)
offset = models.IntegerField()
length = models.IntegerField()
style = models.CharField(max_length=90)
class EntityRange(models.Model):
block = models.ForeignKey(Block, on_delete=models.CASCADE)
offset = models.IntegerField()
length = models.IntegerField()
key = models.IntegerField()
class Data(models.Model):
data = models.TextField()
class EntityRangeData(Data):
enityrange = models.ForeignKey(EntityRange, on_delete=models.CASCADE)
#Entity map here we go
class EntityEntry(models.Model):
key = models.IntegerField()
block= models.ForeignKey(Block, on_delete=models.CASCADE)
type = models.CharField(max_length=90)
mutability = models.CharField(max_length=90)
class EntityEntryData(Data):
entityentry = models.ForeignKey(EntityEntry, on_delete=models.CASCADE)
```#Serializers.py```
class EntityEntryDataSerializer(serializers.ModelSerializer):
class Meta:
model = models.EntityEntryData
fields = "__all__"
class EntityEntrySerializer(serializers.ModelSerializer):
data = EntityEntryDataSerializer()
class Meta:
model = models.EntityEntry
fields = "__all__"
class EntityRangeDataSerializer(serializers.ModelSerializer):
class Meta:
model = models.EntityRangeData
fields = "__all__"
class EntityRangeSerializer(serializers.ModelSerializer):
data = EntityRangeDataSerializer()
class Meta:
model = models.EntityRange
fields = "__all__"
class InlineStyleRangeSerializer(serializers.ModelSerializer):
class Meta:
model = models.InlineStyleRange
fields = "__all__"
class BlockSerializer(serializers.ModelSerializer):
inlineStyleRanges = InlineStyleRangeSerializer(many=True, required=False)
entityRanges = EntityRangeSerializer(many=True, required=False)
class Meta:
model = models.Block
fields = "__all__"
class PostSerializer(serializers.ModelSerializer):
blocks = BlockSerializer(many=True, required=False, read_only=True)
class Meta:
model = models.Post
fields = "__all__"
The output be like
[
{
"id": 1,
"created_by": 1,
"cat": 2
}
]
The Block model does not have attributes called inlineStyleRanges or entityRanges, so you should either use related_name in the ForeignKey field or you could specify a source argument in the serializer.
inlineStyleRanges = InlineStyleRangeSerializer(many=True, required=False, source='inlinestyle_set')
entityRanges = EntityRangeSerializer(many=True, required=False, source='entityrange_set')
You might have to include both nested serializers in BlockSerializer.Meta.fields as well. Instead of "__all__", use a list:
fields = ['id', 'post', 'key', 'inlineStyleRanges', 'entityRanges']
i am developing a rest API using django rest framework and i am stuck at a serializer the idea is to serialize a self recursive many to many model using a through table my code is:
model.py:
class Patient(models.Model):
class Meta:
db_table = 'patients'
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, unique=True)
id_card = models.CharField(max_length=45)
dob = models.DateField()
gender = EnumChoiceField(enum_class=Gender)
patientscol = models.CharField(max_length=45)
fk_user = models.ForeignKey(Users, related_name='user_patient', on_delete=models.CASCADE)
relative = models.ManyToManyField("self", through='PatientHasRelative')
class PatientHasRelative(models.Model):
class Meta:
db_table = 'patients_has_relatives'
fk_patient = models.ForeignKey(Patient, related_name='patient_has', on_delete=models.CASCADE)
fk_relative_patient = models.ForeignKey(Patient, related_name='patient_relative', on_delete=models.CASCADE)
relationship = EnumChoiceField(enum_class=Relationship)
my serializer.py is:
class PatientSerializer(serializers.ModelSerializer):
class Meta:
model = Patient
fields = ('__all__')
id = serializers.UUIDField(read_only=True)
id_card = serializers.CharField(required=True, max_length=45)
dob = serializers.DateField(required=True)
gender = EnumChoiceField(enum_class=Gender)
fk_user = serializers.PrimaryKeyRelatedField(required=True, queryset=Users.objects.all())
relative = PatientSerializer(read_only=True, required=True)#problem is here i cant use PatientSerializer here
class PatientHasRelativeSerializer(serializers.ModelSerializer):
class Meta:
model = PatientHasRelative
fields = ('__all__')
fk_patient = serializers.PrimaryKeyRelatedField(required=True, queryset=Patient.objects.all())
fk_relative_patient = serializers.PrimaryKeyRelatedField(required=True, queryset=Patient.objects.all())
relationship = EnumChoiceField(enum_class=Relationship)
a little help would be appreciated
To accomplish this you need to define related_name in the source model on the source field ie add
class Patient(models.Model):
relatives = models.ManyToManyField(
"self", through='PatientHasRelative', related_name='patients')
with this related_name you can easily access -- add/delete/set relatives/patients on either side of the relationships in the serializers
You can either do this using intermediary model
relative = Patient(**key_value_fields)
patient = Patient(**key_value_field)
PatientHasRelative.objects.create(
relative=relative, patient=patient, through_defaults=(relationship ='value',))
or you can do this
relative.patients.add(patient, through_defaults=relationship ='value')
or this
patient.relatives.add(relative, through_defaults=relationship ='value')
example retrieving
patient.relatives.all()
I would like to do something like the following:
models.py
class Container(models.Model):
size = models.CharField(max_length=20)
shape = models.CharField(max_length=20)
class Item(models.Model):
container = models.ForeignKey(Container)
name = models.CharField(max_length=20)
color = models.CharField(max_length=20)
class ItemSetting(models.Model):
item = models.ForeignKey(Item)
attribute_one = models.CharField(max_length=20)
attribute_two = models.CharField(max_length=20)
serializers.py
class ItemSettingSerializer(serializers.ModelSerializer):
class Meta:
model = ItemSetting
class ItemSerializer(serializers.ModelSerializer):
settings = ItemSettingSerializer(many=True)
class Meta:
model = Item
fields = ('name', 'color', 'settings')
class ContainerSerializer(serializers.ModelSerializer):
items = ItemSerializer(many=True)
class Meta:
model = Container
fields = ('size', 'shape', 'items')
When I do nesting of only one level (Container and Item) it works for me. But when I try to add another level of nesting with the ItemSetting, it throws an AttributeError and complains 'Item' object has no attribute 'settings'
What am I doing wrong?
Multiple nested serialization works for me. The only major difference is that I specify a related_name for the FK relationships. So try doing this:
class Item(models.Model):
container = models.ForeignKey(Container, related_name='items')
class ItemSetting(models.Model):
item = models.ForeignKey(Item, related_name='settings')
Hope this works for you.
i'm recently moving my db to a model Inheritance structure. Here an example:
Task model
STATUS_CHOISE = (('PR', 'In process'), ('ST', 'Stopped'), ('FN', 'Finished'), ('DL', 'Deleted'),)
class Task(models.Model):
owner = models.ForeignKey(User)
process = models.ForeignKey(Process)
title = models.CharField(max_length=200, default='')
description = models.CharField(max_length=1000, default='')
date_created = models.TimeField(auto_now_add=True, auto_now=False)
date_deadline = models.DateTimeField(default=lambda: (datetime.now() + timedelta(days=7)), auto_now_add=False)
parameters = jsonfield.JSONField()
objects = InheritanceManager()
status = models.CharField(max_length=2, choices=STATUS_CHOISE, default='ST')
here the HumanTask that extends Task
PLATFORMS = (('CC', 'CrowdComputer'), ('MT', 'Amazon Mechancial Turk'),)
class HumanTask(Task):
number_of_instances = models.IntegerField(default=1)
uuid = models.CharField(max_length=36, default='')
page_url = models.URLField(max_length=400, default='', null=True, blank=True)
platform = models.CharField(max_length=2,choices=PLATFORMS, default='CroCo')
validation=models.OneToOneField(ValidationTask)
reward = models.OneToOneField(Reward, null=True, blank=True)
now, how should i create the Form? Should i use ModelForm for both classes?
The point is: there are fields that have to be exclude
for example, TaskForm is:
class TaskForm(ModelForm):
owner = forms.ModelChoiceField(queryset=User.objects.all(),widget=forms.HiddenInput)
process = forms.ModelChoiceField(queryset=Process.objects.all(),widget=forms.HiddenInput)
class Meta:
model = Task
exclude = ('date_deadline', 'date_created','parameters','status','objects')
so what i want for the HumanTaskForm is that the exclude are inherited from the TaskForm
i tried with this
class HumanTaskForm(TaskForm):
class Meta:
model= HumanTask
exclude = 'uuid'
but does not work.
Summing up: is this correct? should i use Inheritance for forms? and, how can i have excluded fields, and others parameters, Inheritance?
If you want to leverage the exclude from TaskForm in HumanTaskForm and extend it, you can inherit the Meta class from TaskForm:
class HumanTaskForm(TaskForm):
class Meta(TaskForm.Meta):
model = HumanTask
exclude = TaskForm.Meta.exclude + ('uuid',)
You need to inherit the parent Meta as well as.
The child class will inherit/copy the parent Meta class. Any attribute explicitly set in the child meta will override the inherited version. To my knowledge there is no way to extend the parent Meta attributes (ie adding to 'exclude').
class AwesomeForm(forms.ModelForm):
class Meta:
model = AwesomeModel
exclude = ('title', )
class BrilliantForm(AwesomeForm)
class Meta(AwesomeForm):
model = BrilliantModel
.
print(AwesomeForm.Meta.model)
> AwesomeModel
print(BrilliantForm.Meta.model)
> BrilliantModel
print(AwesomeForm.Meta.exclude)
> ('title', )
print(BrilliantForm.Meta.exclude)
> ('title', )
You could do something like this:
class BrilliantForm(AwesomeForm)
class Meta(AwesomeForm):
model = BrilliantModel
exclude = AwesomeForm.Meta.exclude + ('uuid', )
.
print(BrilliantForm.Meta.exclude)
> ('title', 'uuid')