How can I translate django models without use verbose_name argument on each field? Occur a practicing to inherit all models by an intermediate class which looking for adding new fields and automatically supply it an arg verbose_name by name of field? Or is a best practice to integrate translation into the forms? Maybe I should supply every model verbose_name attribute on create?
I don't want to do like this:
class Currency(models.Model):
name=models.CharField(max_length=32, verbose_name=_("name"))
symbol=models.CharField(max_length=32, verbose_name=_("symbol"))
iso_4217=models.CharField(max_length=3)
def __unicode__(self):
return self.name
class Meta:
verbose_name=_(u'currency')
verbose_name_plural=_(u'currencies')
I want to do something like this:
class BaseModel(models.Model):
__setattr__(self, name, value):
''' Some needles parts of method was cutted out to make it easily
'''
value.verbose_name=_(name)
value.verbose_name_plural=_("%ss" % name)
self.super(BaseModel, name, value)
class Currency(models.Model):
''' Every field of this class whould be supplied by verbose_name and verbose_name_plural
'''
name=models.CharField(max_length=32)
symbol=models.CharField(max_length=32)
iso_4217=models.CharField(max_length=3)
Maybe django has something solution in self?
I think you need to read this article.
To localize your app, you need to wrap every eglish string to an argument of a function ugettext, e.g. 'foo' -> ugettext('foo').
Then you can run manage.py makemessages --locale=<locale>, where <locale> is a language code.
It makes a file inside ./locale/language_code/*.po and you need to translate string in this file to your language, e.g.
#: env_website/lib/python2.6/site-packages/Django-1.3.1-py2.6.egg/django /conf/global_settings.py:44
msgid "Arabic"
msgstr "Арабский"
After that you need to run command manage.py compilemessages
That's it.
You can override Model's add_to_class class method. It is called for every added field.
See how contribute_to_class in fields sets verbose_name from attribute name if it is not provided.
However, I would not recommend this approach as gettext could not create translations catalogs for this fields.
Related
Considering the following models:
class ManySide(django.db.models.Model):
one_side = models.ForeignKey(
to=OneSide, on_delete=models.PROTECT, related_name="related_one_side"
)
class OneSide(django.db.models:model):
# not containing any field relevant here
def many_side_elements(self):
pass # ?
What should I include in method many_side_elements so that calling it from a OneSide Model instance would list a queryset of ManySide elements?
Official docs imply that given o is a OneSide isntance, o.many_side_set.all() should work but it returns an error in shell.
My current solution is the following:
from django.apps import apps
[...]
def many_side_elements(self):
ManySideModel = apps.get_model('<app_name_here>', 'ManySide')
val = ManySideModel.objects.filter(one_side=self)
But I'm concerned it's ineffective since it requires importing the other Model. Actually it caused a circular dependency error message in my project, hence the get_model usage.
Is there any better way? Or xy_set should work in the first place? Then what am I doing wrong?
If you create the model field with a related name, the related name overrides the _set queryset.
In your case
o.related_one_side.all() should work without the need for another def.
See: https://docs.djangoproject.com/en/4.0/topics/db/queries/#following-relationships-backward
You can override the FOO_set name by setting the related_name parameter in the ForeignKey definition. For example, if the Entry model was altered to blog = ForeignKey(Blog, on_delete=models.CASCADE, related_name='entries'), the above example code would look like this: b.entries.all()
I'm new to Django. In the admin panel, the name of the models has an extra "s" at the end. How can I fix that?
Why is django-admin adding an 's'
There is a naming convention in Django the models are named in the singular. So your classes should be named:
class Comment(models.Model):
...
class Note(models.Model):
...
An instance of such a class represents a single comment, not a set of comments. Likewise consider the following
# doesn't really make sense
Comments.objects.get(...)
# reads much better
Comment.objects.get(...) #e.g. get from the comment objects
This is so much of a convention that Django admin expects for your classes to be named in the singular. For this reason, Django adds on an 's' as a standard way of pluralising your class names. (This will admittedly not work for all words and is very anglo-centric, but as an override, Django provides the verbose_name_plural meta-attribute).
How to fix it:
Rename your classes so they are in the singular. This will make your code a lot more readable to anyone who is used to reading django. It will also make your models named in a way consistent with other third-party packages. If your class names are not in English, or do not pluralise simply by appending an s, use verbose_name_plural e.g:
Class Cactus(models.Model):
class Meta:
verbose_name_plural = 'Cacti'
Add a meta class to your model and set verbose_name_plural (https://docs.djangoproject.com/en/3.2/ref/models/options/#verbose-name-plural). Ideally name your classes as singular (in this case you'd have class Comment(models.Model).
class Comments(models.Model):
class Meta:
verbose_name_plural = 'Comments'
Good read:
https://simpleisbetterthancomplex.com/tips/2018/02/10/django-tip-22-designing-better-models.html#:~:text=Always%20name%20your%20models%20using,not%20a%20collection%20of%20companies.
I have a Django model with a TextField. When I use a ModelForm to collect data to add/edit this model, I get a textarea in the HTML form, but I'd like to use a file input instead.
I think what I need to do is to set the widget for this field on my form class using django.forms.FileInput, or maybe a subclass of that input, but it isn't clear to me how to make this work (and I'm not positive this is the right approach).
So, how can I use a file input to collect data for a model using a ModelForm?
Here's what I came up with, elided and condensed for succinctness:
from django.contrib.gis.db.models import Model, TextField
from django import forms
class Recipe(Model):
source = TextField(null=False, blank=True)
...
class AsTextFileInput(forms.widgets.FileInput):
def value_from_datadict(self, data, files, name):
return files.get(name).read()
class RecipeForm(forms.ModelForm):
class Meta:
model = Recipe
fields = ('source', ...)
widgets = {'source': AsTextFileInput(), ...}
Is there a way of defining a label for the field on Django Models? My problem(I'm announcing that I'm a Python Beginner, and also a English beginner): I have this field
source = models.ForeignKey('Objects',name="Object Source",null=True,blank=True,related_name="Src")
So when I execute manage.py migrate, it creats a column on Postgres with ObjectSource name(according to field 'name'). I would like that the name on my database stays 'Source' and the name on my form stays 'Object Source'.
I could just change on my forms file, but on my forms I simply have:
class MyForm(forms.ModelForm):
class Meta:
model = MyTable
If there's a way of defining this on model, would save me a lot of work, cause then I wouldn't have to define each line on my forms.
Just use the verbose_name parameter to the field.
If a django model contains a foreign key field, and if that field is shown in list mode, then it shows up as text, instead of displaying a link to the foreign object.
Is it possible to automatically display all foreign keys as links instead of flat text?
(of course it is possible to do that on a field by field basis, but is there a general method?)
Example:
class Author(models.Model):
...
class Post(models.Model):
author = models.ForeignKey(Author)
Now I choose a ModelAdmin such that the author shows up in list mode:
class PostAdmin(admin.ModelAdmin):
list_display = [..., 'author',...]
Now in list mode, the author field will just use the __unicode__ method of the Author class to display the author. On the top of that I would like a link pointing to the url of the corresponding author in the admin site. Is that possible?
Manual method:
For the sake of completeness, I add the manual method. It would be to add a method author_link in the PostAdmin class:
def author_link(self, item):
return '%s' % (item.id, unicode(item))
author_link.allow_tags = True
That will work for that particular field but that is not what I want. I want a general method to achieve the same effect. (One of the problems is how to figure out automatically the path to an object in the django admin site.)
I was looking for a solution to the same problem and ran across this question... ended up solving it myself. The OP might not be interested anymore but this could still be useful to someone.
from functools import partial
from django.forms import MediaDefiningClass
class ModelAdminWithForeignKeyLinksMetaclass(MediaDefiningClass):
def __getattr__(cls, name):
def foreign_key_link(instance, field):
target = getattr(instance, field)
return u'%s' % (
target._meta.app_label, target._meta.module_name, target.id, unicode(target))
if name[:8] == 'link_to_':
method = partial(foreign_key_link, field=name[8:])
method.__name__ = name[8:]
method.allow_tags = True
setattr(cls, name, method)
return getattr(cls, name)
raise AttributeError
class Book(models.Model):
title = models.CharField()
author = models.ForeignKey(Author)
class BookAdmin(admin.ModelAdmin):
__metaclass__ = ModelAdminWithForeignKeyLinksMetaclass
list_display = ('title', 'link_to_author')
Replace 'partial' with Django's 'curry' if not using python >= 2.5.
I don't think there is a mechanism to do what you want automatically out of the box.
But as far as determining the path to an admin edit page based on the id of an object, all you need are two pieces of information:
a) self.model._meta.app_label
b) self.model._meta.module_name
Then, for instance, to go to the edit page for that model you would do:
'../%s_%s_change/%d' % (self.model._meta.app_label, self.model._meta.module_name, item.id)
Take a look at django.contrib.admin.options.ModelAdmin.get_urls to see how they do it.
I suppose you could have a callable that takes a model name and an id, creates a model of the specified type just to get the label and name (no need to hit the database) and generates the URL a above.
But are you sure you can't get by using inlines? It would make for a better user interface to have all the related components in one page...
Edit:
Inlines (linked to docs) allow an admin interface to display a parent-child relationship in one page instead of breaking it into two.
In the Post/Author example you provided, using inlines would mean that the page for editing Posts would also display an inline form for adding/editing/removing Authors. Much more natural to the end user.
What you can do in your admin list view is create a callable in the Post model that will render a comma separated list of Authors. So you will have your Post list view showing the proper Authors, and you edit the Authors associated to a Post directly in the Post admin interface.
See https://docs.djangoproject.com/en/stable/ref/contrib/admin/#admin-reverse-urls
Example:
from django.utils.html import format_html
def get_admin_change_link(app_label, model_name, obj_id, name):
url = reverse('admin:%s_%s_change' % (app_label, model_name),
args=(obj_id,))
return format_html('%s' % (
url, unicode(name)
))