i have a model that includes a key "service_id" and the values stored in database are like "10001", i want to add a method to admin page that instead of displaying the id it displays a custom value like "Car Wash".
I think you need to create a custom field to be added in your list_display
from django.contrib import admin
#admin.register(MyModelClass)
class MyModelClassAdmin(admin.ModelAdmin):
list_display = ('my_field')
def my_field(self, obj):
return obj.description().title()
my_field.short_description = "My field"
You can change the value returned by the my_field method as you prefer (take into consideration that you can use the obj instance).
Create an Admin Class following
class ModelclassNameAdmin(admin.ModelAdmin):
list_display = ('service__name', [other fields])
in it for list display use double underscore __ for accessing field inside foreign key like service__name
and while registering the model add admin as second argument.
admin.site.register(ModelclassName, ModelclassNameAdmin)
Related
admin.py
#admin.register(StudentsEnrollmentRecord)
class StudentsEnrollmentRecord(admin.ModelAdmin):
list_display = ('Student_Users', 'School_Year', 'Report')
ordering = ('Education_Levels',)
list_filter = ('Student_Users',)
I just want that to add the html link in the adminsite then if the admin click the "report" it will filter what studentenrollmentrecord selected to html file
You need to create a method to calculate this field for each entry in the list, as follows:
from django.utils.html import format_html
#admin.register(StudentsEnrollmentRecord)
class StudentsEnrollmentRecord(admin.ModelAdmin):
list_display = ('Student_Users', 'School_Year', 'report')
...
def report(self, obj):
return format_html(f'<a href={pass_link_here}>Report</a>')
Those kinds of methods take actual objects as a parameter, you can use it to create different links for different objects. obj.field_name
When a field is null in the database, Django inserts "(None)" to hold the place of the null when displaying a changelist. While descriptive, when there are lots of fields on a changelist, it makes everything very busy to look at, where as a blank field would be just as helpful but much less cluttered. So is there some way to change the text Django uses for representing null fields in the changelist? Doesn't matter if the solution is modeladmin-specific or admin wide.
Should also add that I am aware of the solution where you define custom fields and then output a blank string. That works, but in makes the column unsortable, and that is a priority before display, so it's not an option.
Starting with Django 1.9 this behavior has changed and some custom functionality was added to support a solution to the situation you describe.
With Django 1.9, Django now uses a '-' (dash) instead of "(None)" to show NULLs in the Django admin.
In addition you can now customize this default '-' (dash) display for the Django admin with the empty_value_display : globally, for a specific admin class or a specific field:
Globally:
# In settings.py to show '???' instead of '-' for all null django admin values
from django.contrib import admin
admin.site.empty_value_display = '???'
For all fields in a Django admin class:
# admin.py to show "Unknown Item field" instead of '-' for null values in all Item fields
class ItemAdmin(admin.ModelAdmin):
empty_value_display = 'Unknown Item field'
For a single field:
# admin.py to show "No known price" instead of '-' just for null price values in Item
class ItemAdmin(admin.ModelAdmin):
list_display = ('name','price_view')
def price_view(self, obj):
return obj.price
price_view.empty_value_display = 'No known price'
You can override the individual ModelAdmin behavior with this workaround:
from django.contrib.admin.views import main
...
...
...
class MyModelAdmin(admin.ModelAdmin):
def __init__(self,*args,**kwargs):
super(MyModelAdmin, self).__init__(*args, **kwargs)
main.EMPTY_CHANGELIST_VALUE = '-'
Note: Overriding the __init__() is incompatable with the #admin.register() decorator, instead you will need to call admin.site.register(Model, ModelAdmin) after the Model class is defined.
Say I have a model called MyUser. It has some field, and one of them is this one:
user = OneToOneField(User, related_name='more_user_information')
I want to make a view to update this model, and I do the following:
Class AccountEdit(LoginRequiredMixin, UpdateView):
model = MyUser
form_class = MyUserForm
template_name = 'accounts/edit.html'
def get_object(self, queryset=None):
return self.model.objects.get(user=self.request.user)
Each field in MyUser renders fine for editing, except user. This one to one field becomse a select drop down box. What I like to do is to edit the fields on User model like first name or last name.
How can I achieve this while extending UpdateView? or perhaps shuold I use a FormView?
thanks
This problem is actually nothing to do with class based views or update view - its a basic issue that has been there since the beginning, which is:
ModelForms only edit the fields for one model, and don't recurse into
foreign keys.
In other words, if you have a model like this:
class MyModel(models.Model):
a = models.ForeignKey('Foo')
b = models.ForeignKey('Bar')
c = models.ForeignKey('Zoo')
name = models.CharField(max_length=200)
A model form will render three select fields, one for each foreign key, and these select fields will have all the values from those models listed - along with one text field for the name.
To solve this problem, you need to use InlineFormSets:
Inline formsets is a small abstraction layer on top of model formsets.
These simplify the case of working with related objects via a foreign
key.
You should use InlineFormSet from the excellent django-extra-views app. To do this, you'll create a view for the related object as well:
class MyUserInline(InlineFormSet):
model = MyUser
def get_object(self):
return MyUser.objects.get(user=self.request.user)
class AccountEditView(UpdateWithInlinesView):
model = User
inlines = [MyUserInline]
Another option is django-betterforms's Multiform and ModelMultiForm.
Example:
class UserProfileMultiForm(MultiForm):
form_classes = {
'user': UserForm,
'profile': ProfileForm,
}
It works with generic CBV (CreateView, UpdateView, WizardView).
Two of my model's fields are "title" and "summary". Right now, "title" is the first field in the ModelAdmin's list_display, which makes it link to the change page. I have some other fields in list_display as well.
I'd like to make the admin change list page display "summary" under "title" in plain, unlinked text, in the same column as "title". Is this possible? I'm using Django 1.1.
Thanks
Kind of. You can setup your own custom list_display objects to use. So for example, in your case you may do something like so:
def title_and_summary(obj):
return "%s %s" % (obj.title, obj.summary)
Then within your admin class:
class MyAdmin(admin.ModelAdmin):
list_display = (title_and_summary,)
More information can be found on the list_display documentation.
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)
))