Showing extra field on ManyToMany relationship in Django serializer - python

I have a ManyToMany field in Django, like this:
class Dictionary(models.Model):
traditional = models.CharField(max_length=50)
simplified = models.CharField(max_length=50)
pinyin_numbers = models.CharField(max_length=50)
pinyin_marks = models.CharField(max_length=50)
translation = models.TextField()
level = models.IntegerField()
frequency = models.IntegerField()
idiom = models.BooleanField()
child_char = models.ManyToManyField('Dictionary', through='DictionaryChildChar', null=True)
class Meta:
db_table = 'dictionary'
indexes = [
models.Index(fields=['simplified', ]),
models.Index(fields=['traditional', ]),
]
class DictionaryChildChar(models.Model):
class Meta:
db_table = 'dictionary_child_char'
from_dictionary = models.ForeignKey(Dictionary, on_delete=models.CASCADE, related_name="from_dictionary")
to_dictionary = models.ForeignKey(Dictionary, on_delete=models.CASCADE, related_name="to_dictionary")
word_order = models.IntegerField()
Currently, I have a serializer like this:
class FuzzySerializer(serializers.ModelSerializer):
pinyin = serializers.CharField(
required=False, source="pinyin_marks")
definition = serializers.CharField(
required=False, source="translation")
hsk = serializers.CharField(required=False, source="level")
class Meta:
model = Dictionary
fields = ["id", "simplified", "pinyin", "pinyin_numbers","definition", "hsk", "traditional", "child_char"]
depth = 1
This gives me a dictionary entry, as well as the child dictionary entries associated with it (as a Chinese word is made up of several Chinese characters)
However, I need to know what order these child characters are in, and hence why I have word_order.
I would like this word_order field to appear on the individual child_char - how do I write my serializer in such a way that this additional field is present? Would I need to make a separate serializer for child_char?
EDIT: I have tried this serializer, it doesn't work:
class FuzzyChildCharSerializer(serializers.ModelSerializer):
class Meta:
model = DictionaryChildChar
fields = ["word_order"]

Easiest way is to create a dedicated FuzzyChildCharSerializer and then connect it to your original serializer as a nested relationship:
class FuzzyChildCharSerializer():
class Meta:
model = DictionaryChildChar
fields = ["word_order"] # And whatever other fields you want
class FuzzySerializer():
child_char = FuzzyChildCharSerializer(many=True)
...
You could also write a SerializerMethodField.

It appears I had to bridge the connection via the glue table, which makes sense.
class FuzzyChildCharSerializer(serializers.ModelSerializer):
pinyin = serializers.CharField(
required=False, source="pinyin_marks")
definition = serializers.CharField(
required=False, source="translation")
hsk = serializers.CharField(required=False, source="level")
class Meta:
model = Dictionary
fields = ["id", "simplified", "pinyin", "pinyin_numbers","definition", "hsk", "traditional",]
class FuzzyChildCharSerializerGlue(serializers.ModelSerializer):
to_dictionary = FuzzyChildCharSerializer()
class Meta:
model = DictionaryChildChar
fields = '__all__'
class FuzzySerializer(serializers.ModelSerializer):
pinyin = serializers.CharField(
required=False, source="pinyin_marks")
definition = serializers.CharField(
required=False, source="translation")
hsk = serializers.CharField(required=False, source="level")
from_dictionary = FuzzyChildCharSerializerGlue(many=True)
class Meta:
model = Dictionary
fields = ["id", "simplified", "pinyin", "pinyin_numbers","definition", "hsk", "traditional", "from_dictionary"]
depth = 1
This provides each character with its given word order

Related

Django - UniqueConstraint not created

I am trying to enforce a constraint for mysql where user is prohibited from inserting twice the same name and model. E.g This should not be allowed to be inserted twice: name:Name1 model:Model1
#Model
class Car(models.Model):
name = models.CharField(max_length=100)
model = models.CharField(max_length=100)
#View
class CarListCreateAPIView(generics.ListCreateAPIView):
serializer_class = CarSerializer
def get_queryset(self):
trip_code = self.kwargs.get("pk")
return Car.objects.filter(trip = trip_code) #Return cars for given trip
#Seializer
class CarSerializer(serializers.ModelSerializer):
class Meta:
model = Car
fields = ('__all__')
constraints = [
models.UniqueConstraint(fields=['name', 'model'], name='car_name_model_constraint')
]
The problem is that the constraint is never created and thus not enforced. What might be the issue with the code?
Use unique_together in class Meta in Model like that:
class Car(models.Model):
name = models.CharField(max_length=100)
model = models.CharField(max_length=100)
class Meta:
unique_together = ['name','model']
More on that here: https://docs.djangoproject.com/en/4.0/ref/models/options/#unique-together

DRF polymorphic many-to-many with extra fields

I'm tinkering around with Django Rest Framework to build an api that can handle Bills of Materials (BoM) of electronic components.
I'm using django-polymorphic and django-rest-polymorphic so I can use polymorphic models for the components (they have shared attributes, but I still want to handle them in much the same way. The polymorphic models are great for this purpose).
All is well and good until I introduce many-to-many relations with a 'through' attribute. What I would like is a BoM that has several different components, each with a quantity, e.g. BoM1 has 2x470k resistor & 3x 100uF capacitor.
models.py: (pruned a bit to keep this post from being an epic novel)
class BoM(models.Model):
"""Bill of Materials: a list of all parts and their quantities for a given pedal"""
pedal = models.ForeignKey(Pedal, on_delete=models.CASCADE)
variant = models.CharField(max_length=100, blank=True)
electronic_components = models.ManyToManyField(
'ElectronicComponent', through='ElectronicComponentQty', blank=True)
class Meta:
verbose_name = 'Bill of materials'
verbose_name_plural = 'Bills of materials'
def __str__(self):
return str(self.pedal)
class ElectronicComponent(PolymorphicModel):
"""Shared data model for electronic components"""
value = models.CharField(max_length=20)
datasheet = models.FileField(upload_to='uploads/components', blank=True)
def __str__(self):
return self.value
class ElectronicComponentQty(models.Model):
"""Combination of resistor and quantity"""
bom = models.ForeignKey(BoM, on_delete=models.CASCADE)
component = models.ForeignKey(
ElectronicComponent, on_delete=models.CASCADE)
quantity = models.PositiveIntegerField(default=1)
class Meta:
verbose_name = 'Elecronic component quantity'
verbose_name_plural = 'Electronic component quantities'
class Resistor(ElectronicComponent):
"""Resistor data model"""
WATTAGES = [('1/4w', '1/4w'), ('1/8w', '1/8w')]
wattage = models.CharField(max_length=4, choices=WATTAGES, default='1/4w')
class Capacitor(ElectronicComponent):
"""Capacitors (all kinds)"""
VOLTAGE_RATINGS = [
('16V', '16V'),
('35V/50V', '35V/50V'),
]
CAP_TYPES = [
('ceramic disk', 'ceramic disk'),
('film', 'film'),
('electrolytic', 'electrolytic'),
('tantalum', 'tantalum'),
('other', 'other'),
]
capacitor_type = models.CharField(
max_length=20, choices=CAP_TYPES, default='film')
voltage_rating = models.CharField(
max_length=10, choices=VOLTAGE_RATINGS, blank=True)
serializers.py:
class ElectronicComponentSerializer(serializers.ModelSerializer):
class Meta:
model = ElectronicComponent
fields = '__all__'
class ElectronicComponentQtySerializer(serializers.ModelSerializer):
class Meta:
model = ElectronicComponentQty
fields = '__all__'
class BoMSerializer(serializers.ModelSerializer):
electronic_components = ElectronicComponentQtySerializer(
many=True, read_only=True)
class Meta:
model = BoM
fields = '__all__'
class ResistorSerializer(serializers.ModelSerializer):
class Meta:
model = Resistor
fields = '__all__'
class CapacitorSerializer(serializers.ModelSerializer):
class Meta:
model = Capacitor
fields = '__all__'
class ElectronicComponentPolySerializer(PolymorphicSerializer):
model_serializer_mapping = {
Resistor: ResistorSerializer,
Capacitor: CapacitorSerializer,
}
With this code I can create EletronicComponentQty objects no problem. However, when I try to list the BoM (through the serializer), I get:
AttributeError at /pedalparts/boms/
Got AttributeError when attempting to get a value for field bom on
serializer ElectronicComponentQtySerializer.
The serializer field might be named incorrectly and not match any
attribute or key on the Capacitor instance.
Original exception text was: 'Capacitor' object has no attribute
'bom'.
Anyone know how I can solve this? I'm open to any changes that make this work.
As electronic_components on the BoM model refers to the ElectronicComponent model it should not use the ElectronicComponentQtySerializer but one that can serialize the right instances, most likely the ElectronicComponentSerializer or the ElectronicComponentPolySerializer.

How to serialize a one to many relation in django-rest using Model serializer?

These are my models and serializers. I want a representation of Question Model along with a list of people the question was asked to.
I am trying this:
#api_view(['GET', 'PATCH'])
def questions_by_id(request,user,pk):
question = Question.objects.get(pk=pk)
if request.method == 'GET':
serializer = QuestionSerializer(question)
return Response(serializer.data)
But I get an empty dictionary ({}). However when I remove the asked field from QuestionSerializer I get a complete representation of Question along with Places serialized nicely. What am I missing ?
class AskedToSerializer(serializers.ModelSerializer):
class Meta:
model = AskedTo
fields = ('to_user', 'answered')
class QuestionSerializer(serializers.ModelSerializer):
class Meta:
model = Question
places = PlaceSerializer(many=True, required=False)
asked = AskedToSerializer(source='askedto_set', many=True)
fields = ('id', 'created_on', 'title', 'places', 'answered','asked')
extra_kwargs = {'created_by': {'read_only': True}}
class Question(BaseModel):
title = models.CharField(max_length=200, null=False)
places = models.ManyToManyField(Place, blank=True)
answered = models.BooleanField(default=False)
class AskedTo(BaseModel):
ques = models.ForeignKey(Question, on_delete=models.CASCADE)
to_user = models.ForeignKey(settings.AUTH_USER_MODEL)
replied = models.BooleanField(default=False)
class Place(models.Model):
g_place_id = models.CharField(max_length=20,primary_key=True)
json = models.TextField(null=True)
name = models.CharField(max_length=40)
I figured it out. There were two errors.
Changed this:
class AskedToSerializer(serializers.ModelSerializer):
class Meta:
model = AskedTo
fields = ('to_user', 'answered')
to this (notice the change in fields, fields on model and serializer didn't match)
class AskedToSerializer(serializers.ModelSerializer):
class Meta:
model = AskedTo
fields = ('to_user', 'replied')
Secondly, I needed to define any extra fields outside class Meta
class QuestionSerializer(serializers.ModelSerializer):
places = PlaceSerializer(many=True, required=False)
asked = AskedToSerializer(source='askedto_set', many=True)
class Meta:
model = Question
fields = ('id', 'created_on', 'title', 'places', 'answered','asked')
extra_kwargs = {'created_by': {'read_only': True}}
Notice the change in definition of places and asked.
In my case, I have this models.py:
class Section(models.Model):
title = models.CharField(max_length=500)
description = models.TextField(blank=True)
user = models.ForeignKey(Profile, related_name="sections", on_delete=models.CASCADE)
class Feed(models.Model):
title = models.CharField(max_length=500)
link_rss = models.URLField(max_length=500)
link_web = models.URLField(max_length=500)
description = models.TextField(blank=True)
language = models.CharField(max_length=50, blank=True)
logo = models.URLField(blank=True)
sections = models.ManyToManyField(Section, related_name="feeds")
And I completed the serializers.py this:
class FeedSerializer(serializers.ModelSerializer):
sections = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = Feed
fields = '__all__'
class SectionSerializer(serializers.ModelSerializer):
feeds = FeedSerializer(many=True, read_only=True)
class Meta:
model = Section
exclude = ('user',)

MongoEngine: Embeddeddocument fields do not take default value as none?

I've set the default values for fields in embedded document but when I try to post data it doesn't accept None or Blank values.
Here is what my code looks like-
models.py
class MetaData(EmbeddedDocument):
adcode = StringField(max_length=50, default="", blank=True, Null=True)
additional_html_below_header = StringField(max_length=50, default="")
adhoc_plus_disable_pacing = BooleanField(default=False)
adhoc_plus_has_priority = BooleanField(default=False)
adhoc_server = StringField(max_length=50, default="")
class LandingPage(Document):
type = StringField(max_length=50, default="")
meta_clean_URL_tag = StringField(max_length=50, default="")
meta_name = StringField(max_length=50, default="")
created_time = DateTimeField(default=datetime.datetime.now)
new = BooleanField(default=False)
meta_data = EmbeddedDocumentField(MetaData)
serializers.py
class MetaDataSerializer(serializers.EmbeddedDocumentSerializer):
class Meta:
model = MetaData
class LandingPageSerializer(serializers.DocumentSerializer):
meta_data = MetaDataSerializer()
class Meta:
model = LandingPage
Is there anything wrong I'm doing here?
class MetaDataSerializer(serializers.EmbeddedDocumentSerializer):
adcode = serializers.CharField(allow_blank=True,allow_null=True)
adhoc_server = serializes.CharField(allow_blank=True,allow_null=True)
additional_html_below_header = serializers.CharField(allow_blank=True,allow_null=True)
class Meta:
model = MetaData
DRF-mongoengine or for that matter DRF does not allow null and blank values for strings. They need to be explicitly mentioned. The bounds imposed on models do not hold inside serializers.
The above mentioned change should help you maintain the validations as required by you.

Django Multiple GenericRelation related_name in admin

I'm tring to create a generic foreign key and reuse it multiple time inside the same model
The problem is that when I load the admin interface I got the 2 Inline fields that display the same data.
I tried to use an fk_name passing the related_name defined in the model but without luck
Here is the code any help is appreciate
model.py:
class TranslatedCharField(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey()
class Meta:
unique_together = ('object_id', 'lang')
lang = models.CharField(max_length=255, choices=LANGUAGES)
text = models.CharField(max_length=255)
class Evento(models.Model):
slug = generic.GenericRelation(TranslatedCharField, related_name="slug")
titolo = generic.GenericRelation(TranslatedCharField, related_name="title")
admin.py:
class TranslatedSlugInline(generic.GenericTabularInline):
extra = 0
model = TranslatedCharField
verbose_name = "Slug"
verbose_name_plural = "Slug"
class TranslatedTitoloInline(generic.GenericTabularInline):
extra = 1
model = TranslatedCharField
verbose_name = "Titolo"
verbose_name_plural = "Titolo"
class EventoAdmin(admin.ModelAdmin):
inlines = [
TranslatedSlugInline,
TranslatedTitoloInline,
]
admin.site.register(Evento, EventoAdmin)
this is the result:
as you can see the list of TranslatedCharField is duplicated for the 2 relations
unique_together = ('object_id', 'lang')
I think you want
unique_together = ('content_type', 'object_id', 'lang')
because object_id is not unique across models.
slug = generic.GenericRelation(TranslatedCharField, related_name="slug")
titolo = generic.GenericRelation(TranslatedCharField, related_name="title")
"related_name" is how the TranslatedCharField can access in the other direction, i.e. by which attribute name you want it to access that Evento. "slug_event" and "title_event" more fitting.
ok I'd reviewed the things a but and the result is the follow:
class TranslatedCharField(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey()
relation_field = models.CharField(max_length=255)
class Meta:
unique_together = ('object_id', 'lang', 'relation_field')
lang = models.CharField(verbose_name="lingua", max_length=255, choices=LANGUAGES)
text = models.CharField(verbose_name="testo", max_length=255)
def translatedFieldInlineAdminFormFactory(relation_field, translatemodel):
class TranslatedFieldInlineAdminForm(forms.ModelForm):
class Meta:
model = translatemodel
def clean(self):
self.cleaned_data["relation_field"] = relation_field
return self.cleaned_data
class TranslatedTabularInline(generic.GenericTabularInline):
def __init__(self, *args, **kwargs):
super(TranslatedTabularInline, self).__init__(*args, **kwargs)
self.form = translatedFieldInlineAdminFormFactory(self._relation_field_filter, self.model)
extra = 1
max_num = len(LANGUAGES)
exclude = ('relation_field',)
model = None
_relation_field_filter = None
return TranslatedFieldInlineAdminForm
In this way when I need multiple TranslatedCharField I do the following
on the admin:
class TranslatedSlugInline(TranslatedTabularInline):
model = TranslatedCharField
_relation_field_filter = "slug_event_portal"
verbose_name = "Slug"
verbose_name_plural = "Slug"
class TranslatedSlugInline(TranslatedTabularInline):
model = TranslatedCharField
_relation_field_filter = "slug2_event_portal"
verbose_name = "Slug2"
verbose_name_plural = "Slug2"
thanks to the clean method a specific _relation_field_filter is used for each inline
this allow me at least to save them distinguished and retrive them useing the custom queryset
I'm still working on the Model of the Evento to retrive the specific set of values instead the whole related fields but I think I'm not too far now

Categories