How do Meta field names correspond to model attributes - django

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

Related

Django tabularInline 'categories.Category_children_ids' has more than one ForeignKey to 'categories.Category'. You must specify a 'fk_name' attribute

I'm want to create nested categories, model work fine.
class Category(models.Model):
category_name = models.CharField(max_length=100)
children_ids = models.ManyToManyField(
"Category", blank=True, related_name="categories"
)
...etc
but, when i add inline for admin panel
class ChildrensInline(admin.TabularInline):
model = Category.children_ids.through
compiler shows me error:
'categories.Category_children_ids' has more than one ForeignKey to 'categories.Category'. You must specify a 'fk_name' attribute.
I also try fk_name='categories', and columns name inside Category_children_ids table, but it not work
The value for fk_name has to be the name of the class (lower-case), prefixed with either "from_" or "to_", depending on your needs. So in your case, it has to be either fk_name='from_category' or fk_name='to_category':
class ChildrensInline(admin.TabularInline):
model = Category.children_ids.through
fk_name='from_category' # or: 'to_category'
Generally, a quick way to figure such things out is to (transiently) place a simple print(model.__dict__) pretty much anywhere in your code, here e.g. right after the second line. Then all fields of model will be shown in the console output.

Django Model verbose_name as API rows response names

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']

What does related_name do?

In the Django documentation about related_name it says the following:
The name to use for the relation from the related object back to this one. It’s also the default value for related_query_name (the name to use for the reverse filter name from the target model). See the related objects documentation for a full explanation and example. Note that you must set this value when defining relations on abstract models; and when you do so some special syntax is available.
If you’d prefer Django not to create a backwards relation, set related_name to '+' or end it with '+'.
I didn't understand it clearly. If somebody would please explain it a bit more, it would help me a lot.
When you create a foreign key, you are linking two models together. The model with the ForeignKey() field uses the field name to look up the other model. It also implicitly adds a member to the linked model referring back to this one.
class Post(models.Model):
# ... fields ...
class Comment(models.Model):
# ... fields ...
post = models.ForeignKey(Post, related_name=???)
There are three possible scenarios here:
1. Don't specify related_name
If you don't specify a name, django will create one by default for you.
some_post = Post.objects.get(id=12345)
comments = some_post.comment_set.all()
The default name is the relation's name + _set.
2. Specify a custom value
Usually you want to specify something to make it more natural. For example, related_name="comments".
some_post = Post.objects.get(id=12345)
comments = some_post.comments.all()
3. Prevent the reverse reference from being created
Sometimes you don't want to add the reference to the foreign model, so use related_name="+" to not create it.
some_post = Post.objects.get(id=12345)
comments = some_post.comment_set.all() # <-- error, no way to access directly
related_query_name is basically the same idea, but when using filter() on a queryset:
posts_by_user = Post.objects.filter(comments__user__id=123)
But to be honest I've never used this since the related_name value is used by default.
If in a model you have a ForeignKey field (this means you point through this field to other model):
class Author(models.Model):
name = ......
email = .....
class Article(models.Model):
author = models.ForeignKey(Author)
title= ....
body = ....
if you specify related_name on this field
class Article(modles.Model):
author = models.ForeignKey(Author, related_name='articles')
you give a name to the attribute that you can use for the relation (named reverse realationship) from the related object back to this one (from Author to Article). After defining this you can retrieve the articles of an user like so:
author.articles.all()
If you don't define a related_name attribute, Django will use the lowercase name of the model followed by _set (that is, in our case, article_set) to name the relationship from the related object back to this one, so you would have to retrieve all articles of an user like so:
author.article_set.all()
If you don't want to be possible a reverse relationship (from the model to which points your ForeignKey filed to this model (the model in which the ForeignKey field is defined) you can set
class Author(models.Model):
author = models.ForeignKey(User, related_name='+')

Django forms with odd model relationship

I am working with an existing database that I can not modify and having some trouble trying to deal with presenting forms for modifying the database in Django. The structure in question is as follows and all models are unmanaged.
class Persons(models.Model):
personid = models.BigIntegerField(primary_key=True, db_column='PersonID')
....
class Phones(models.Model):
phoneid = models.BigIntegerField(primary_key=True, db_column='PhoneID')
number = models.CharField(max_length=60, db_column='Number', blank=True)
type = models.CharField(max_length=15, db_column='Type', blank=True)
...
class Personsphones(models.Model):
personphoneid = models.BigIntegerField(primary_key=True, db_column='PersonPhoneID')
personid = models.ForeignKey(Persons, db_column='PersonID')
phoneid = models.ForeignKey(Phones, db_column='PhoneID')
...
I want to create a form to display all of the 'Phones' associated with a particular 'Persons' and in addition be able to modify/add/remove 'Phones' belonging to a 'Persons'. Right now the only thing I can think of is to display the 'Phones' in a modelformset and then if one is added or removed manually set the 'Personsphones' relation. Any ideas on how to best deal with this model setup?
For making changes to your models you may want to use django-south http://south.aeracode.org/docs/
As far as displaying your 'Phone' under your forms.py you may want to set up class meta like so. With this any changes made to models will reflect on change
class Meta:
model = Persons
exclude = ('user')
In models you may want to use Foreignkey fore relationships between phones and Persons. Better seen in action here https://docs.djangoproject.com/en/dev/ref/models/fields/#foreignkey

Validating an Autocomplete field in Django

I have models similar to the following:
class Band(models.Model):
name = models.CharField(unique=True)
class Event(models.Model):
name = models.CharField(max_length=50, unique=True)
bands = models.ManyToManyField(Band)
and essentially I want to use the validation capability offered by a ModelForm that already exists for Event, but I do not want to show the default Multi-Select list (for 'bands') on the page, because the potential length of the related models is extremely long.
I have the following form defined:
class AddEventForm(ModelForm):
class Meta:
model = Event
fields = ('name', )
Which does what is expected for the Model, but of course, validation could care less about the 'bands' field. I've got it working enough to add bands correctly, but there's no correct validation, and it will simply drop bad band IDs.
What should I do so that I can ensure that at least one (correct) band ID has been sent along with my form?
For how I'm sending the band-IDs with auto-complete, see this related question: Django ModelForm Validate custom Autocomplete for M2M, instead of ugly Multi-Select
You can override the default fields in a ModelForm.
class AddEventForm(forms.ModelForm):
band = forms.CharField(max_length=50)
def clean_band(self):
bands = Band.objects.filter(name=band,
self.data.get('band', ''))
if not bands:
raise forms.ValidationError('Please specify a valid band name')
self.cleaned_data['band_id'] = bands[0].id
Then you can use your autocomplete widget, or some other widget. You can also use a custom widget, just pass it into the band field definition: band = forms.CharField(widget=...)