Django admin filter - django

I've got multiple instances of a model, and each instance has a related email address. However, several instances have the same connected email address but when I put filter['email'] into my admin.py, I get a long list of the instances' emails, i.e. multiple copies of the same email in several cases.
Is there a way I can remove emails being listed multiple times? Or a way of customising the filter view into something a little nicer? (drop down menu maybe?)
I don't have a ManyToManyField relationship currently, or anything like that. I just have instances in my database with the fields name and email. My models.py looks like this:
import ldapdb.models
from ldapdb.models.fields import CharField, IntegerField, ListField
class Item(ldapdb.models.Model):
item = CharField(db_column='item', max_length=30, primary_key=True, unique=True)
email = CharField(db_column='mail', max_length=20)
My admin.py looks like so:
from items.models import Item
from django.contrib import admin
class ItemAdmin(admin.ModelAdmin):
readonly_fields = ('email',)
list_display = ('item', 'email')
list_filter = ['email']
search_fields = ['item']
admin.site.register(Item, ItemAdmin)
Obviously I've been looking at https://docs.djangoproject.com/en/1.3/ref/contrib/admin/ but I can't really see much by the way of customising my admin's filter view.

Can you post some of your code? I'm not entirely sure I understood the relationship between the instances to your email - is it an email field? a ForeighKey to a different model? how is there more than one if it's not a ManyToMany or similar relationship? And how is the filtering done in the admin?
EDIT
Ok now I understand the problem. What you want is not possible. See for the django admin site the fact that they are the same email doesn't matter because it's still a different object. There's no way around that without either specifying that field to be unique or messing with the admin site code.
A better solution would be to configure the email as searchable in the admin model and then when you search for email example#example.com it would bring all matches back.
Another good solution is to make email a different model and link it to the Item model through a ManyToMany relationship. Then you create an EmailAdmin with a method that shows you all related items for each email.
It all depends on what you actually need. Ultimately you might want to write your own view or mess around with the admin site to modify it to what you need.
Also, you might want to change the email from CharField to EmailField. Hope this helps!

Related

extending default User model in Django

I've written my first application Django 2.0.
Everything is working fine and the application is almost ready when I realized to replace id primary key field from default integer type to UUID to make database entry more secure.
When I searched for this how to change id of user table to UUID I got many tutorials extending AbstractBaseUser.
Here is I have written own User model.
account/models.py
class User(AbstractBaseUser):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
But I'm confused more with examples on different sources.
Every example is adding few more fields in extended model like
first_name
last_name
is_staff
is_admin
active
and functions as
def get_fullname(self):
def get_shortname(self):
etc.
I think all these fields and functions are there by default in AUTH_USER_MODEL.
Does extending AbstractBaseUser overwrites AUTH_USER_MODEL and it is required to add all fields which is there by default?
also, I'm using settings.AUTH_USER_MODEL as foreign key in different models. Should It be replaced by account.User model?
I'm also using django-allauth plugin to enable login using the social network and use email only for authentication. Do I require to add email field in the extended model with unique=True?
Django AbstractBaseUser provides only following fields: password, last_login, is_active. So if you are using custom User model inherited from AbstractBaseUser you need to define all other fields such as email manually.
As another part of question just adding AUTH_USER_MODEL = 'users.User' to your settings.py file should make everything works without replace code in your project.
UPD
If you need field like first_name, last_name, etc. to be includet to the model you can use AbstractUser instead of AbstractBaseUser.
As the Django documentation indicates, it's difficult to extend the User table after-the-fact, and not recommended at all for apps. A better way is to create an auxiliary table which has a 1:1 relationship with the user-id. Leave Django's user-table alone and just use this other table to pony-up to it.
The "Django Annoying" project, at https://github.com/skorokithakis/django-annoying#autoonetoonefield, has some very useful "juice" to make this much easier: an AutoOneToOneField. Whereas Django's foreign-key field will throw an error if an record doesn't exist, this field will automagically create one on-the-fly, thereby side-stepping the entire issue. (The documentation page linked-to above shows exactly how this is done.)

Django Admin Reverse Relations

If you are on the django admin page for the model Group. You don't know that there is a reverse relation to user.
Some people (not me) have difficulties with it.
Is there a way to show all reverse relations, so that you can jump to the matching admin pages?
Example:
On admin page for Group I want a link to User (and all other models which refer to it).
This should happen by code, not by hand with templates.
This method doesn't automatically add links to all related models of a Group, but does for all Users related to a Group (so for one related model at a time). With this you'll get an inline view in your Group with the related Users.
You could probably extend this technique to make it automatically work for all related fields.
class UserInline(admin.StackedInline):
model = User
extra = 0
readonly_fields = ('change',)
def change(self, instance):
if instance.id:
# Django's admin URLs are automatically constructed
# based on your Django app and model's name.
change_url = urlresolvers.reverse(
'admin:djangoapp_usermodel_change', args=(instance.id,)
)
return '<a class="changelink" href="{}">Change</a>'.format(change_url)
else:
return 'Save the group first before editing the user.'
change.allow_tags = True
class GroupAdmin(admin.ModelAdmin):
list_display = ('name',)
inlines = (UserInline,)
You might also be interested in this extension I created for Django admin pages to link to related objects:
https://github.com/gitaarik/django-admin-relation-links
It's quite easy to use and makes the admin a lot more convenient to use :).

Django form with ManyToMany field with 500,000 objects times out

Lets say for example I have a Model called "Client" and a model called "PhoneNumbers"
class PhoneNumbers(models.Model):
number = forms.IntegerField()
class Client(models.Model):
number = forms.ManyToManyField(PhoneNumbers)
Client has a ManyToMany relationship with PhoneNumbers. PhoneNumbers has almost 500,000 records in it so when it comes to editing a Client record from a model form with a MultiSelect widget that comes with a M2M filed, it takes forever to load. In fact, it never does. It just sits there trying to load all of those phone objects I am assuming.
My workaround was to so some tedious things with ajax and jquery to edit only the phone numbers in a Client record. Before wasting my time with all of that I wanted to see if there is somehow another way to go about it without having my page hang.
You need to create a custom widget for this field that lets you autocomplete for the correct record. If you don't want to roll your own: http://django-autocomplete-light.readthedocs.io/
I've used this for its generic relationship support, the M2M autocomplete looks pretty easy and intuitive as well. see video of use here: http://www.youtube.com/watch?v=fJIHiqWKUXI&feature=youtu.be
After reading your comment about needing it outside the admin, I took another look at the django-autocomplete-light library. It provides widgets you can use outside the admin.
from dal import autocomplete
from django import forms
class PersonForm(forms.ModelForm):
class Meta:
widgets = {
'myformfield': autocomplete.ModelSelect2(
# ...
),
}
Since Django 2.0, Django Admin ships with an autocomplete_fields attribute that generates autocomplete widgets for foreign keys and many-to-many fields.
class PhoneNumbersAdmin(admin.ModelAdmin):
search_fields = ['number']
class ClientAdmin(admin.ModelAdmin):
autocomplete_fields = ['number']
Note that this only works in the scope of Django admin of course. To get autocomplete fields outside the admin you would need an extra package such as django-autocomplete-light as already suggested in other answers.
Out of the box, the model admin has a raw_id_fields option that let your page load much quicker. However, the user interface of raw id fields isn't very intuitive, so you might have to roll your own solution.
We use this 3rd party widget for this:
https://github.com/crucialfelix/django-ajax-selects
Btw, your 'example' above is really bad DB design for a bunch of reasons. You should just have the phone number as a text field on the Client model and then you would have none of these issues. ;-)

Filter foreignkey choices in Admin changelist view

I got two models which are related via ForeignKey in Django. (I am using Django 1.3)
Class Person(models.Model):
# some fields here like name, gender, etc...
Class Course(models.Model):
# some fields here
contact = models.ForeignKey(Person, blank=True, null=True)
In the admin changelist view for the Courses I want to be able to filter the Courses by the ForeignKey contact. In the admin.py i got:
class CourseAdmin(admin.ModelAdmin):
list_filter = ('contact',)
This work very well. I can filter the Courses by all available Contacts. Now I would like to display only those contacts that actually have a Course attached to them.
I read here on SO to implement CustomFilters by creating a custom FilterSpec. I dont know wheter this is the right direction, because I only need to further filter the queryset that is used to display the choices for the contacts.
In the shell I get the desired queryset by this:
contacts=Person.objects.filter(course__in=Course.objects.all()).distinct()
I already read that you can easily achieve this in 1.4, but I am still bound to 1.3
Can someone please point me into the right direction? Thank you!
Django 1.3 also supports Filters, but the filter classes were moved/renamed in 1.4. And using a FilterSpec is the way to achieve your goal. You not only need to filter the queryset, but to handle properly the applied filter from QueryString. So go ahead with filters. Here is a very good snippet, that handles the FK filtering, and has decent options: http://djangosnippets.org/snippets/2260/

Django admin: search for foreign key objects rather than <select>?

My model looks like this:
class Asset(models.Model):
serial_number = models.CharField(max_length=100, unique=True)
asset_tag = models.CharField(max_length=100, unique=True)
class WorkOrder(models.Model):
asset = models.ForeignKey(Asset)
Essentially, a work order is submitted and then an admin assigns an asset to the work order. The asset_tag field is a barcode that we can scan in. When editing the work order in the Django admin, by default the asset field is displayed as a <select> widget. What we want to be able to do is have a search field so that we can scan the asset tag and then search for the right asset in the DB to associate with the work order.
I know you can customize the Django admin foreign key to a hard coded query, but I can't figure out how to get it so it does a search based on a field on the admin page.
Did you take a look at raw_id_fields?
It should be pretty to close to what you're after.
If you are using Django >= 2.0, you can take advantage of a feature called autocomplete_fields. You must define search_fields on the related object’s ModelAdmin because the autocomplete search uses it.
Since you have a ForeignKey relationship to Asset in WorkOrder, in the admin.py of your app add the following:
from django.contrib import admin
#admin.register(Asset)
class AssetAdmin(admin.ModelAdmin):
search_fields = ["serial_number", "asset_tag"]
#admin.register(WorkOrder)
class WorkOrderAdmin(admin.ModelAdmin):
autocomplete_fields = ["asset"]
Add the fields you want to use for searching to search_fields, and add define autocomplete_fields as shown in the code above.
Now you can use the autocomplete_fields from django 2.0.
It's quite neat.