I have a model with field defined as
p = models.DecimalField(max_digits=12, decimal_places=6)
But the decimal_places=6 is only for internal accuracy. I only want 2 decimal_places to show up in the admin fields. So I thought perhaps I could do something with properties. But properties don't show up as fields in the Admin interface. E.g.: database record: 100.256754
admin edit field shows: 100.26
Is there a way to do this?
I'm with wlashell -- your edit form should display the raw value; otherwise, you'd be indirectly modifying the value each time the object got modified, even if the actual field wasn't changed at all. Probably not what you want.
If you'd simply like to change the way the field displays in the change list (for readability, perhaps), you can define a method on your model class like so:
from django.db import models
from django.template.defaultfilters import floatformat
class MyModel(models.Model):
p = models.DecimalField(max_digits=12, decimal_places=6, verbose_name='My Field Name')
...
def display_p(self):
"""Format `p` to 2 decimal places, for readability."""
return floatformat(self.p, 2)
display_p.short_description = 'My Field Name'
display_p.admin_order_field = 'p'
Then, in your ModelAdmin subclass list_display you'd include display_p instead of p:
from django.contrib import admin
from myapp.models import MyModel
class MyModelAdmin(admin.ModelAdmin):
list_display = ['display_p', ...]
...
admin.site.register(MyModel, MyModelAdmin)
HTH.
If you want to simply display the value of the field but not make it editable you could use a custom template, or some javascript to mask / alter the value.
I have to question the wisdom though, of hiding the remaining digits from someone looking at the data. This smells like a disaster in the making as a person editing a field like that if you hid the values would not know what the full value is. If you really don't care about that from the user side, why store it in that manner at all.
Related
I created a project and within the project an app call myusers. I then created a model with the class name AllUsers. I then populated the model with data from faker.
When I go to 127.0.0.1/admin under authorization I have groups and users. Under myusers I have ‘All userss’ which is a link to http://127.0.0.1:8000/admin/myusers/allusers/
so, I’m just wondering if this is a minor bug. Shouldn’t it say ‘AllUsers’ and not ‘All userss’? Or did I corrupt something along the way?
No, a model normally has a singular name, so AllUser, not AllUsers.
Django will, based on the name add a suffix …s for the plural. Indeed, for a model you can check the verbose name with:
>>> from django.contrib.auth.models import User
>>> User.Meta.verbose_name_plural
'users'
For your model you can specify the singluar and plural name yourself with:
class AllUsers(models.Model):
# …
class Meta:
verbose_name = 'all user'
verbose_name_plural = 'all users'
But nevertheless, it is better to give the class a singular name. Especially since other parts, like the default for related_name=… [Django-doc] is modelname_set, so for a ForeignKey of your AllUser model, that would look like allusers_set, which does not look very pleasant.
You should not create a model that represents a set of entities. A model should represent a single entity. Since a model should only represent a single entity, Django adds an "s" to the end in the admin to pluralize it (i.e, a model named "Car" will be "Cars" in the admin).
Of course you can change the verbose_plural_name (https://docs.djangoproject.com/en/3.1/ref/models/options/#verbose-name-plural), but the best way forward is to not continue using this technique to represent all users at all.
To represent all users you should be using a QuerySet.
I am trying to implement django-localflavors into my Django app.
I import USStateSelect & USZipCodeField at the beginning of my models.py and then include them as a field in my model along with other fields, like so:
from localflavor.us.forms import USStateSelect, USZipCodeField
...
Class MyModel(models.Model):
...
state = USStateSelect()
zip_5 = USZipCodeField()
However, when I go to Django admin and try to create a new Model object, I see every other field I wrote (CharFields, etc.) EXCEPT any of the localflavor fields. They are simply not showing up at all as an input field in my Model object form. I have done migrations on my database so that is not the issue.
Am I misunderstanding how to use django-localflavor? I read in an answer to a different post that localflavor doesn't actually create input fields, only stores data... but then I've also read that it DOES let you input data. At this point I am confused. Any help would be appreciated!
I think what you are looking for are the model fields. The form fields are used when building your own forms (usually outside the admin, such as a contact form). Localflavor has a couple fields that should do what you need. Note that these are essentially CharFields that have some extra validation to make sure the follow the desired format.
You need to specify choices option.
Change your code a little as below:
from localflavor.us.forms import USStateSelect, USZipCodeField
...
Class MyModel(models.Model):
...
state = USStateSelect(choices=STATE_CHOICES) # add choices
zip_5 = USZipCodeField() # no change on this line
I have a model which represents a work order. One of the fields is a DateField and represents the date the work order was created (aptly named: dateWOCreated). When the user creates a new work order, I want this dateWOCreated field to be automatically populated with todays date and then displayed in the template. I have a few other fields that I want to set without user's intervention but should show these values to the user in the template.
I don't want to simply exclude these fields from the modelForm class because there may be a need for the user to be able to edit these values down the road.
Any help?
Thanks
When you define your model, you can set a default for each field. The default object can be a callable. For your dateWOCreated field we can use the callable date.today.
# models.py
from datetime import date
class WorkOrder(models.Model):
...
dateWOCreated = models.DateField(default=date.today)
To display dates in the format MM/DD/YYYY, you need to override the widget in your model form.
from django import forms
class WorkOrderModelForm(forms.ModelForm):
class Meta:
model = WorkOrder
widgets = {
'dateWOCreated': forms.DateInput(format="%m/%d/%Y")),
}
In forms and model forms, the analog for the default argument is initial. For other fields, you may need to dynamically calculate the initial field value in the view. I've put an example below. See the Django Docs for Dynamic Initial Values for more info.
# views.py
class WorkOrderModelForm(forms.ModelForm):
class Meta:
model = WorkOrder
def my_view(request, *args, **kwargs):
other_field_inital = 'foo' # FIXME calculate the initial value here
form = MyModelForm(initial={'other_field': 'other_field_initial'})
...
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)
))
I have a model formset on a model with a couple of date fields - which again is a DateTimeField in the model.
But when it displays in the template, it is shown as a text field.
How can I show it as a drop down box ?
WFormSet = inlineformset_factory(UserProfile, W, can_delete=False,exclude=[<list of fields to be excluded>], extra=3)
This is how I am intializing the modelformset.
How do I override these settings
The usual way to do this is to override the default field types in your ModelForm definition.
The example below would work if you had a DateField in your model (I note you have a DateTimeField... I'll come back to that in a sec). You're going to be specifying the exact same field type as would normally be specified, but you'll pass a different widget to the form field constructor.
from django.db import models
from django import forms
from django.forms.extras import SelectDateWidget
class MyModel(models.Model):
a_date_field = models.DateField()
class MyModelForm(forms.ModelForm):
a_date_field = forms.DateField(widget=SelectDateWidget())
class Meta:
model = MyModel
There isn't, to my knowledge, an equivalent widget provided for the DateTimeField in Django 1.0.x. In this case, you'll want to make a custom widget, perhaps subclassing SelectDateWidget. I note from a quick google on SelectDateTimeWidget that there have been several others who've been making what appear to be the widget you're seeking. I've not used them, so caveat emptor, but something like this SelectDateTimeWidget patch might be a good place to start.
Edit: When using a ModelFormset or InlineModelFormset, you can still achieve this by passing form=MyModelForm to the inlineformet_factory function:
MyModelFormset = inlineformset_factory(MyParentModel, MyModel, form=MyModelForm)
This works because the two model formset factories actually call the regular formset_factory constructor in their own implementations. It's a little hard to figure out because it's not explicitly stated in the docs... rather the Django docs allude to this ability by mentioning in passing that the model formset factories extend the normal formset_factory. Whenever I'm in doubt, I open django/forms/models.py to check out the full signature of the formset factory functions.