Django admin list filter - django

I want to add a custom model method to the admin filter, however it fails.
Example Foo:
class Foo(models.Model):
number = models.IntegerField()
def big_enough(self):
return self.number > 99
now at the admin panel:
class FooAdmin(admin.ModelAdmin):
list_filter = ('number', 'big_enough')
Fails, I get the error
ImproperlyConfigured at /admin/test/foo/
'FooAdmin.list_filter[0]' refers to field 'big_enough' that is missing from model 'Foo'.

See this SO thread. It's not as easy as it feels like it should be.

You cannot use a model method for this purpose. list_filter is used to filter a django queryset, which cannot meaningfully utilize bare functions.

Related

Django ClassBasedViews - Generic Views

I get a strange Error: 'ListView' must define 'queryset' or 'model'
Here is my ListView:
class ProjectListView(ListView):
template_name = "ProjectList.html"
model = Project
context_object_name = "projects"
I have the same problem with a CreateView. Is there any other information need to find a solution?
How to fix this error.
You must define a get_queryset() function in each generic ListView view.
That's how you tell django what objects you want to display.
Model is NOT your project. It must be set to a model class.
Say, you have a model called Poll in your models.py file. And you want to display all polls in the db, using the generic ListView. Then you could write this:
model = Poll
That equals to this query:
Poll.objects.all()
If you want to override this and show objects e.g. with publication date during the last week, then you define a get_queryset() with nedeed query parameters:
ProjectListView(generic.ListView):
# ...
def get_queryset(self):
return obj.objects.filter(pub_date__gte=timezone.now() - datetime.timedelta(days=7))
And so on.

Can I create a custom django modelchoicefield with a default queryset

I have an order model with a followed_by field:
class order(models.Model):
followed_by = models.ForeignKey(User, limit_choices_to={'groups__name': "Managers"})
I have several such models and forms for those models. By default the form displays a modelchoicefield listing users that are mangers. This is fine. But the display isn't nice: it gives the username, and I want first+last name. This would work nicely: Change Django ModelChoiceField to show users' full names rather than usernames
except that now in everyform I must declare the queryset to limit users to managers. Can I use the above method so that the custom modelchoicefield defaults to my filtered queryset. so then from a form I can just say:
followed_by = ManagerUserModelChoiceField()
Can you define the queryset on your ModelChoiceField child class?
class UserModelChoiceField(ModelChoiceField):
# Query that returns set of valid choices
queryset = User.objects.filter(group__name='Managers')
def label_from_instance(self, obj):
return obj.get_full_name()
Try passing in the queryset as an argument to the ManagerUserModelChoiceField class.
followed_by = ModelChoiceField(queryset = User.objects.filter(groups__name="Managers")
After my comment to #Enrico this thought occurred to me: I overwrote the "init" class on my custom field like so:
class UserModelChoiceField(forms.ModelChoiceField):
def __init__(self, *args, **kwargs):
super(UserModelChoiceField, self).__init__(queryset=User.objects.filter(groups__name="Managers"), *args, **kwargs)
I've seen stuff like this done in python before but I'm new to python so I'm not sure if this is a bad thing to do or if I should make this better somehow? I'd appreciate some feedback. That being said, it seems to be working correctly.

ManyToManyField widget in a django admin change list?

In the change list of the django admin, I want to use list_editable to display a django-autocomplete widget for a ManyToManyField.
I found something similar here: list_editable and widgets
Normally including a ManyToManyField in list_display raises an ImproperlyConfigured exception, eg:
""'BookAdmin.list_display[2]', 'author' is a ManyToManyField which is not supported."
I (perhaps unwisely) removed 3 lines from contrib/admin/validate.py to bypass the exception. :)
I now have it spitting out the following, which is close(?) but no cigar.
<django.db.models.fields.related.ManyRelatedManager object at 0x1032a85d0>
Any thoughts on how to proceed? Is there a better way of doing this?
Here's what I have at the moment: (AuthorAutocomplete is working fine in the regular admin form)
class AuthorAutocomplete(AutocompleteSettings):
search_fields = ('first_name', 'last_name')
class BookAdmin(AutocompleteAdmin, admin.ModelAdmin):
def get_changelist_form(self, request, **kwargs):
kwargs.setdefault('form', AuthorAutocompleteForm)
return super(BookAdmin, self).get_changelist_form(request, **kwargs)
list_display = ['isbn', 'title', 'author', 'publisher']
#...
class AuthorAutocompleteForm(forms.ModelForm):
class Meta:
model = Book
author = AuthorAutocomplete
Thanks!
To get the values for a ManyToMany field in your own code so you can display their values you can do the following. I'm using list_display as an example.
class TestAdmin(admin.ModelAdmin):
def get_some_value(self):
return ", " . join([x.__str__() for x in self.manytomany_field.all()])
get_some_value.short_description = 'Title' # sets column header title
list_display = ('get_some_value',) # define Model's fields to be presented on admin changelist
where manytomany_field is the name you gave your manytomany_field and get_some_value is a method within the class which is assigned a short_description.
Silly me. I thought that William's manytomany_field was a built-in ModelAdmin object. So I ran his code as is (after putting in the missing parenthesis after join).
Strange to say, it ran without producing an error message. But (None) appeared when I was supposed to get values. And I thought it was strange that I couldn't find anything when I Googled "django model.Admin.manytomany_field". Hah!
So eventually I realized that I was to put the NAME of the many-to-many field in place of manytomany_field. It works!

Django admin: Default model display problem

In django models, if we have
def __unicode__(self): then it will be used as how you want to display the model by default
Now in django admin, I want to have a custmized display field(showing this object as an url so can navigate to this object), but I can't change unicode method for it used for other purpose. What do I supposed to do?
You can create a custom method for admin class
class SomeModelAdmin(admin.ModelAdmin):
list_display = ('__unicode__', 'active_status')
def active_status(self, obj):
if obj.profile.is_active:
return """One"""
return """Two"""
active_status.allow_tags = True
active_status.description = ""
This is just very simple example, so you can put your logic into this function
You can also return some html code
Don't use __unicode__ for a purpose like setting a convenience URL.
That will obscure your printed object name for any other purpose.
From which view are you trying to create a link? From the changelist view? From the change view? From a foreign key?
In general, you can simply define any method on your model (or ModelAdmin), that returns a full HTML link <a href=, set allow_tags = True, and refer to it in your admin fields.
# models method
def admin_url(self):
return 'Edit Model' % the_url
admin_url.allow_tags = True
# ModelAdmin method, through ForeignKey
def admin_url(self, obj):
return 'Edit Model' % obj.foreignkey.url
admin_url.allow_tags = True
I agree with those answers, but on my machine just not working.
I was using Python3 and Django1.8, and try to use this.
class MyModel(models.Model):
name = models.CharField(max_length=60)
def __str__(self):
return 'MyModel: {}'.format(self.name)

Foreign keys in django admin list display

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)
))