Django Model verbose_name as API rows response names - django

As I can't save special characters in fields in django model I have to bypass it.
For example: I would like to have "km/h" field. So I'm using it like:
class Unit(models.Model):
kmh = models.FloatField(null=True, db_column='km/h', verbose_name='km/h')
then I have example serializer:
class UnitSerializer(serializers.ModelSerializer):
class Meta:
model = Unit
fields = ['kmh']
and when I'll use it with APIViews response which will include the field will look like:
{
"kmh":10,
}
I would like to make it look like my verbose_name so:
{
"km/h":10
}
How can I do it? I have like 30 of these fields with special characters.

You can define field names with special characters by updating the locals() but I would strongly advise not to do this: in both Python and JavaScript, variable names with spaces make it less convenient to retrieve attributes, update data, etc. In most programming languages and frameworks, this can only make it worse to process the data.
You can define a field, and specify the source=… parameter to specify where to retrieve the data from:
class UnitSerializer(serializers.ModelSerializer):
locals().update({
'km/h': serializers.FloatField(source='kmh', allow_null=True)
})
class Meta:
model = Unit
fields = ['km/h']

Related

Django equivalent of ASP.NET Parameter Binding or Ruby on Rails Action Controller Parameters

I'm wondering what's mentioned in the title. This are links to the examples mentioned, regarding other techs:
ASP.NET Parameter Binding
Ruby on Rails Action Controller
Parameters
Currently I'm building an API using DRF and using custom code in views or serializers validate methods to validate parameters, like this:
class AnimalWriteSerializer(serializers.ModelSerializer):
class Meta:
model = Animal
fields = '__all__'
def validate_dicose_category(self, value):
raise serializers.ValidationError('Dicose Category cannot be set manually.')
Is there a better way?
Since in your example you are telling the serializer to support __all__ fields, then you need to disable updating that one manually.
You probably mean to use use exclude as in the example below, which will simply remove the field from "all". The primary difference between exclude and using read_only is that the output will include the dicose_category.
Use the exclude= to exclude this field. This is the opposite of fields=, and you can only use one at a time.
class AnimalWriteSerializer(serializers.ModelSerializer):
dicose_category = serializers.CharField(read_only=True)
class Meta:
model = Animal
exclude = ["dicose_category"]
You can declare the field as read only (directly or using extra kwarg). You can't write it but it will include be in the output. I'm not sure why you would want to do this, but it can be helpful if you are using the return data for something and need it there.
class AnimalWriteSerializer(serializers.ModelSerializer):
dicose_category = serializers.CharField(read_only=True)
class Meta:
model = Animal
fields = "__all__"
# or declare an extra_kwarg which does the same thing:
class AnimalWriteSerializer(serializers.ModelSerializer):
class Meta:
model = Animal
fields = "__all__"
extra_kwargs = {
"dicose_category": { "read_only": True }
}
And lastly, I strongly suggest listing all the fields you intended to be updated directly, rather than using __all__ or exclude=.
New fields added to the model are not automatically updateable
All updateable fields are explicitly and clearly listed
Unit tests can now be explicit, and the output format is consistent
class AnimalWriteSerializer(serializers.ModelSerializer):
class Meta:
model = Animal
fields = [
"name",
"mission",
"favorite_color",
]

Django REST ModelSerializer --- General Question

I am working through a tutorial that includes the building of an articles app. I have an Article model that I am serializing and I am curious about why I need to explicitly set certain fields when using a ModelSerializer.
Here is my model:
from django.db import models
from core.models import TimestampedModel
class Article(TimestampedModel):
slug = models.SlugField(db_index=True, max_length=255, unique=True)
title = models.CharField(db_index=True, max_length=255)
description = models.TextField()
body = models.TextField()
author = models.ForeignKey('profiles.Profile', on_delete=models.CASCADE, related_name='articles')
def __str__(self):
return self.title
Pretty standard stuff. Next step is to serialize the model data in my serializers.py file:
class ArticleSerializer(serializers.ModelSerializer):
author = ProfileSerializer(read_only=True) # Three fields from the Profile app
description = serializers.CharField(required=False)
slug = serializers.SlugField(required=False)
class Meta:
model = Article
fields = (
'author',
'body',
'createdAt',
'description',
'slug',
'title',
'updatedAt',
)
Specifically, why do I need to explicitly state the author, description, and slug fields if I am using serializers.ModelSerializer and pulling those fields in from my model in my class Meta: below?
Thanks!
In the Django-Rest-Framework documentation, drf-docs/model_serializer/specifying-which-fields-to-include it says:
If you only want a subset of the default fields to be used in a model serializer, you can do so using fields or exclude options, just as you would with a ModelForm. It is strongly recommended that you explicitly set all fields that should be serialized using the fields attribute. This will make it less likely to result in unintentionally exposing data when your models change.
Therefore by using fields = in the Serializer META, you can specify just the needed fields, and not returning vital fields like id, or exessive information like updated and created timestamps.
You can also instead of using fields, use exclude, which again takes in a tuple, but just excludes the fields you don't want.
These are especially useful when your database table contains a lot of information, returning all this information, especially if it is listed, can result in large return JSON's, where the frontend may only use a small percentage of the sent data.
DRF has designed their framework like this to specifically combat these problems.
In my opinion, we should define field in serializer for:
Your api use serializer don't need all data of your models. Then you can limit field can get by serializer. It faster if you have so much data.
You dont want public all field of your model. Example like id
Custom field in serializer like serializers.SerializerMethodField() must define in fields for work
Finally, iF you dont want, you can define serializer without define fields. Its will work normally

How do Meta field names correspond to model attributes

I am writing a Django Model and different Forms to modify the model's data.
For simplification let's say my model is as follows:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
birth_date = models.DateField()
# (...) some other properties
__identity_card_front = models.FileField(blank=True)
class IdentityCardForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('__identity_card_front',)
My question is: must the fields names correspond to the attributes of Profile class (the exact name of the variables definition)? Do these strings determine the HTML form field name that will be searched in form validation? And if so, how can I customise it? I don't want to have to obligatorily call my field name '__identity_card_front', but instead maybe something like 'id_front', 'idf', etc.
I have seen labels might be used, but I was not clear how Django treats fields tags. I could not find either a good explanation on the Docs without getting lost around low-level class definitions and properties.
Note: I am using django-2.0

Using ModelSerializer with joined records

I am trying to make a tool for drawing diagrams on the web. I have a model like so:
class PlaneableItem(Model):
name = models.CharField(max_length=NAME_LENGTH, blank=True)
class View(PlaneableItem):
# Some useful details
class Anchor(Model):
view = models.ForeignKey(View)
planeable = models.ForeignKey(PlaneableItem)
class BlockRepresentation(Anchor):
# Useful details
class LineRepresentation(Anchor):
# Useful details
I try to make a rest API that returns lists of all blocks and lines for a specific view, including the name of the planeable that they refer to.
I can get a queryset for this using:
qs = BlockRepresentation.objects.filter(view=theview).all()
qs.select_related('planeable')
qs.extra(select={'name': 'rest_api_planeableitem.name'})
However, now I can't use a ModelSerializer on it, because the field 'name' is not part of the BlockRepresentation.
I really like ModelSerializers, is there a better way of doing this?
Is there a particular reason you need that extra() call? If the sole purpose of that call is to rename a field, you can omit that from the queryset and rename the field using a SerializerMethodField from your serializer. I will assume planeable is the ForeignKey field in BlockRepresentation model to the PlaneableItem model. Sample code:
from rest_framework import serializers
class BlockRepresentationSerializer(serializers.ModelSerializer):
# Some fields
name = serializers.SerializerMethodField()
class Meta:
model = BlockRepresentation
def get_name(self, obj):
if obj.planeable:
return obj.planeable.name
return ''

django modelform use normal field for relation

I am building a little message system with this model:
class Mail(models.Model):
sender = models.ForeignKey(Character, related_name="+")
to = models.ForeignKey(Character, related_name="+")
sent_at = models.DateTimeField(auto_now_add=True)
subject = models.CharField(max_length=127)
body = models.TextField()
now i made a modelform from this:
class ComposeForm(forms.ModelForm):
class Meta:
model = Mail
exclude = ["folder", "sender", "sent_at"]
however this gives my "to" field a drop down list with all possible characters.
Id like to make this in a normal charfield (later with auto completer) instead of this drop down.
Any idea how i can achieve this?
I've been in a similar place and I found 2 solutions, depending on the needs. The first one is suposing you're going to use something like a select2 and get the query via ajax:
class ComposeForm(forms.ModelForm):
to_char = forms.CharField(max_length=255, required=False) # use the name you want
class Meta:
model = Mail
exclude = ["folder", "sender", "sent_at","to"]
So to_char is empty, and then you manage that field as you want, and when you do the POST, you'll get the value of to_char in the view, and assign to the model where you need it.
The other option I suggest is to use a ModelChoiceField instead of Charfield like this:
class ComposeForm(forms.ModelForm):
to_char = forms.ModelMultipleChoiceField(queryset=Character.objects.all())
class Meta:
model = Mail
exclude = ["folder", "sender", "sent_at","to"]
This will make easier to use an external select tool as select2 (without AJAX)
I choose the first one when the model has thousands of possible choices, so the select hasn't to load all the choices and the page will be fast. I use a select2 in the template over this field, in this case to_char, that loads the options in an AJAX view
I use the second one when there are hundred of choices and using the autocomplete of select2 over this field has no problems, I think if you don't have too many choices this will be the best for you, the ModelChoiceField, you can attach an autocomplete without any trouble
I might have found even a neater solution to this problem. By overriding the default form field in this way:
class ComposeForm(forms.ModelForm):
class Meta:
model = Mail
exclude = ["folder", "sender", "sent_at"]
widgets = {'to': forms.TextInput()}
1) If you do not want to display all the values, then all you need to do is to override to field queryset.
Like:
class ComposeForm(forms.ModelForm):
class Meta:
model = Mail
exclude = ["folder", "sender", "sent_at"]
def __init__(self, *args, **kwargs):
super(ComposeForm, self).__init__(*args, **kwargs)
self.fields['to'].queryset = Character.objects.none()
2) If you want to add autocomplete then you will need view that does the filtering and returns filtered options. You also need some kind of js widget. There are many of those available. On django side you only need to update field widget parameters so your js can pick up the field.