I have some models for a messaging system on my django site.
class Enquiry(models.Model):
etc...
class Message(models.Model):
enquiry = models.ForeignKeyField(Enquiry)
sent_at = models.DateTimeField(default=timezone.now)
etc...
Each message has a foreign key to an enquiry. In the admin site I would like to be able to order the enquiries by the most recent received message. In a regular view, I can do this:
Enquiry.objects.annotate(latest_message=Max('message__sent_at')).order_by('-latest_message')
Is there a way to achieve this in the admin framework?
You can override the get_queryset method for your model admin, and use the same approach that already works in your views.
My first suggestion was to try setting ordering for your model admin, but you reported that it didn't work.
class EnquiryAdmin"
ordering = ['-message__sent_at']
Related
I'm developing a reservation system REST api in Django with a relatively simple model that will determine if a user has a valid membership in order for them to reserve a certain item. When creating a reservation, I have a simple validator written like this:
def validate_member(value):
"""
Validator for a user in a reservation. Will throw a ValidationError if the user does not have an
updated membership.
"""
if not valid_membership(value):
raise ValidationError("User does not have a valid membership!")
This validation is run on the foreign key field in the reservation table, which is written as such:
# The gear item in the reservation
gear_item = models.ForeignKey(Entry,
on_delete=models.SET_NULL,
null=True,
help_text="The gear item being reserved.",
validators=[validate_entry]
)
The serializer for this model is written as a ModelSerializer, like such:
class ReservationSerializer(serializers.ModelSerializer):
class Meta:
model = Reservation
fields = "__all__"
This design works fine for queries in the REST API, but fails for any modifications in the admin console, with the error: 'int' object has no attribute 'rentable'
It seems that validating a foreign key in the admin console passes the primary key integer into the value parameter of the validation function, while the REST API passes in the entire object. Is there a workaround for this, or should I expect to not use the admin console at all due to this limitation?
Best solution/work around for this would be overriding your admin console's form.
I think this code snippet will be enough for you to handle your specific field and it's validation.
Within an app for an online shop I have two simple models for products and deliveries:
class Product(model.models):
delivery = models.ForeignKey(Delivery, on_delete=models.CASCADE)
class Delivery(model.models):
published = models.Datefield()
I am using the build-in Django admin.
class ProductInline(admin.TabularInline):
model = Product
#admin.register(Delivery)
class DeliveryAdmin(admin.ModelAdmin):
inlines = [ProductInline,]
To increase robustness of the app it is very important, that products can't be changed as soon as the related delivery has been published to the customer. So on very attempt to change a product, I need to do some validation to check if the Delivery has been published. Things I have tried:
Create fieldset for InlineAdmin with custom clean()method
Custom clean() method on the model instance
These approaches don't work, though. When implemented, I loose the ability to edit the entire delivery model from the admin panel (I am trying to only limit edits of products). This is because clicking the save Button in the admin Panel will automatically call save() and clean() on the products regardless of weather or not the products were changed.
Has someone encountered a similar problem before?
Maybe, you need to override the form?
class ProductInlineForm(forms.ModelForm):
def clean(self):
# your validation.
# here you can raise ValidationError
return super().clean()
class ProductInline(admin.TabularInline):
form = ProductInlineForm
model = Product
https://docs.djangoproject.com/en/2.2/topics/forms/modelforms/#overriding-the-clean-method
I've extended the standard User with a Profile model using a one-to-one relationship:
class Profile(models.Model):
user = models.OneToOneField(User, primary_key=True, editable=False)
# Additional user information fields
This is in an app called account_custom. The issue is that in the admin site, I would like this model to show up along with User under "Authentication and Authorization".
I was able to make this show up in the right place in the admin by setting Meta.app_label:
class Profile(models.Model):
class Meta:
app_label = 'auth'
db_table = 'account_custom_profile'
I set Meta.db_table so this uses the existing database table, and everything seems to function properly.
The problem is that Django wants to generate migrations for this, one in account_custom to delete the table and one in auth to create it again. I'm guessing this would either fail or wipe out the data depending on the order the migrations are run, but the deeper problem is that it's trying to create a migration in auth which isn't part of my code base.
Is there a way around this, or is there some better way to control where a ModelAdmin shows up in the admin site?
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!
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. ;-)