I got stuck when trying to display User foreign as use full name
models.py
class Customer(models.Model):
name = models.charfield(max_length=100)
sales = models.foreignkey(User, on_delete=models.CASADE)
admin.py
#admin.register(Customer)
list_display = ('name', 'sales')
nah by default sales as user foreign display as username. But i want display it to first name or full name
Edited
You can add a method in the ModelAdmin, as mentioned by #dirkgroten in the comments below:
from django.contrib import admin
#admin.register(Customer)
class CustomerAdmin(admin.ModelAdmin)
list_display = ('name', 'sales_first_name')
def sales_first_name(self, obj):
return obj.sales.first_name + obj.sales_last_name
The upside is you can handle null values as you wish, if your foreign key is not required, e.g.
def sales_first_name(self, obj):
if obj.sales:
return obj.sales.first_name
else:
return None # or return 'nobody'
Old answer
(which, as mentioned in the comments, does not work (unless you use a plugin like django-related-admin))
Maybe use sales__first_name instead?
list_display = ('name', 'sales__first_name')
This way django will evaluate the first_name attribute of the user object instead of returning the default string representation (defined by the __str__ method in the user model, which returns just the username).
Related
I'm trying to include Django-autocomplete-light in my project. Everything works as expected including the creation of new choice by autocomplete. The only problem is my model contains more than one field and I'm using autocomplete only on the 'name' field. When I save my new record django-admin creates new object with the same name in the database instead of updating the record already created by autocomplete. At the end I have two records, one half empty created by autocomplete, and one valid created by django-admin.
models.py
class Montinent(models.Model):
name = models.CharField(max_length=250)
code = models.CharField(max_length=2, unique=True, db_index=True)
created = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('-name',)
def __str__(self):
return self.name
views.py
class MontinentAutocomplete(autocomplete.Select2QuerySetView):
def get_queryset(self):
#Don't forget to filter out results depending on the visitor !
if not self.request.user.is_authenticated():
return Montinent.objects.none()
qs = Montinent.objects.all()
if self.q:
qs = qs.filter(name__istartswith=self.q)
return qs
urls.py
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
url(r'^montinent-autocomplete/$', MontinentAutocomplete.as_view(create_field='name'), name='montinent-autocomplete',),
]
admin.py
class MontinentForm(forms.ModelForm):
name = forms.ModelChoiceField(
queryset = Montinent.objects.all(),
widget = autocomplete.ModelSelect2(url='montinent-autocomplete')
)
class Meta:
model = Montinent
fields = ['name', 'slug', 'code']
class MontinentAdmin(admin.ModelAdmin):
form = MontinentForm
admin.site.register(Montinent, MontinentAdmin)
The way autocomplete creates new choice is as follow:
When the user selects that option, the autocomplete script will make a
POST request to the view. It should create the object and return the
pk, so the item will then be added just as if it already had a PK.
In this case it looks like I need to override the default django-admin save method. I tried just to make the 'name' field unique but in this case Django says this name already exist.
My question is how to override the default save method in my models.py so django to use the returned from autocomplete pk and append the missing information to the row instead of creating new object?
I was also struggling to fill more than one field with the autocomplete create choice. I needed to include the user who is creating the new entry.
The method that saves the new entry into the database is the create_object(text) method from autocomplete.Select2QuerySetView. You can read more about this method in the documentation http://django-autocomplete-light.readthedocs.io/en/master/api.html
So, to include a user I just override the method as follow:
def create_object(self, text):
return self.get_queryset().create(**{self.create_field: text, 'user' : self.request.user})
Now you don't need to have partially filled forms using the autocomplete create options. Just fill it with any field you want to.
I have a django project with a model that looks like:
class Profile(models.Model):
#some other stuff
owner = models.OneToOneField(settings.AUTH_USER_MODEL)
last_modified = models.DateTimeField(default = timezone.now)
def __unicode__(self):
return self.owner.name
__unicode__.admin_order_field = 'owner__last_name'
My model admin looks something like:
class ProfileAdmin(admin.ModelAdmin):
ordering = ['-last_modified']
list_display = ['__unicode__', 'last_modified']
I would like for the admin to be sorted by last_modified by default (as it is now) but to be able to sort alphabetically by clicking on the top of the first column of the list display. I tried to add the __unicode__.admin_order_field line as described here, but that doesn't seem to have made any difference. Is what I want possible? If not why not?
You can only sort fields in the django admin interface if they are fields on your model or if they are fields you custom annotate in the get_queryset method of your ModelAdmin class--essentially fields created at the DB level. However, assuming you are deriving your __unicode__ or __str__ method from some fields on your model (and you are--from owner.name) you should be able to reference those fields and make the column sortable like so (though you could use this method to make the unicode field sortable on any model attribute you'd like):
class ProfileAdmin(admin.ModelAdmin):
def sortable_unicode(self, obj):
return obj.__unicode__()
sortable_unicode.short_description = 'Owner Name'
sortable_unicode.admin_order_field = 'owner__last_name'
ordering = ['-last_modified']
list_display = ['sortable_unicode', 'last_modified']
Though I do find it a bit strange that you will be displaying the owner's name but sorting on last_name. This might be a bit puzzling when you wonder why your sort order doesn't match the displayed name in the admin interface.
A foreign key on a model is not appearing in the Django admin site. This is irrespective of whether the field is explicitly specified in a ModelAdmin instance (fields = ('title', 'field-that-does-not-show-up')) or not.
I realize there are many variables that could be causing this behavior.
class AdvertiserAdmin(admin.ModelAdmin):
search_fields = ['company_name', 'website']
list_display = ['company_name', 'website', 'user']
class AdBaseAdmin(admin.ModelAdmin):
list_display = ['title', 'url', 'advertiser', 'since', 'updated', 'enabled']
list_filter = ['updated', 'enabled', 'since', 'updated', 'zone']
search_fields = ['title', 'url']
The problem is the advertiser foreign key is not showing up in the admin for AdBase
class Advertiser(models.Model):
""" A Model for our Advertiser
"""
company_name = models.CharField(max_length=255)
website = models.URLField(verify_exists=True)
user = models.ForeignKey(User)
def __unicode__(self):
return "%s" % self.company_name
def get_website_url(self):
return "%s" % self.website
class AdBase(models.Model):
"""
This is our base model, from which all ads will inherit.
The manager methods for this model will determine which ads to
display return etc.
"""
title = models.CharField(max_length=255)
url = models.URLField(verify_exists=True)
enabled = models.BooleanField(default=False)
since = models.DateTimeField(default=datetime.now)
expires_on=models.DateTimeField(_('Expires on'), blank=True, null=True)
updated = models.DateTimeField(editable=False)
# Relations
advertiser = models.ForeignKey(Advertiser)
category = models.ForeignKey(AdCategory)
zone = models.ForeignKey(AdZone)
# Our Custom Manager
objects = AdManager()
def __unicode__(self):
return "%s" % self.title
#models.permalink
def get_absolute_url(self):
return ('adzone_ad_view', [self.id])
def save(self, *args, **kwargs):
self.updated = datetime.now()
super(AdBase, self).save(*args, **kwargs)
def impressions(self, start=None, end=None):
if start is not None:
start_q=models.Q(impression_date__gte=start)
else:
start_q=models.Q()
if end is not None:
end_q=models.Q(impression_date__lte=end)
else:
end_q=models.Q()
return self.adimpression_set.filter(start_q & end_q).count()
def clicks(self, start=None, end=None):
if start is not None:
start_q=models.Q(click_date__gte=start)
else:
start_q=models.Q()
if end is not None:
end_q=models.Q(click_date__lte=end)
else:
end_q=models.Q()
return self.adclick_set.filter(start_q & end_q).count()
class BannerAd(AdBase):
""" A standard banner Ad """
content = models.ImageField(upload_to="adzone/bannerads/")
The mystery deepens. I just tried to create a ModelForm object for both AdBase and BannerAd, and both generated fields for the advertiser. Some crazy admin things going on here...
I believe I've just run into exactly the same problem, but was able to debug it thanks to the help of persistent co-workers. :)
In short, if you look in the raw HTML source you'll find the field was always there - it's just that:
Django tries to be clever and put the form field inside a div with CSS class="form-row $FIELD_NAME",
The field's name was "advertiser", so the CSS class was "form-row advertiser",
...Adblock Plus.
Adblock Plus will hide anything with the CSS class "advertiser", along with a hell of a lot of other CSS classes.
I consider this a bug in Django.
maybe it is an encode error. I had the same problem, but when i added # -- coding: UTF-8 -- in the models.py, all fine.
Another very dumb cause of the same problem:
If there is only one instance of the related model, then the filter simply won't show. There is a has_output() method in RelatedFieldListFilter class that returns False in this case.
It's a strange problem for sure. On the AdBase model if you change
advertiser = models.ForeignKey(Advertiser)
to
adver = models.ForeignKey(Advertiser)
then I believe it'll show up.
Powellc, do you have the models registered with their respective ModelAdmin class?
admin.site.register(Advertiser, AdvertiserAdmin) after the ModelAdmin definitions.
You are talking about the list_display option, right?
Is the unicode-method for your related model set?
If the field is a ForeignKey, Django
will display the unicode() of the
related object
Also check this thread for some hints: Can "list_display" in a Django ModelAdmin display attributes of ForeignKey fields?
Try disabling your ad blocker. No, this is not a joke. I just ran into this exact problem.
We just ran into this problem.
It seems that if you call you field advertiser the in the admin the gets given an 'advertiser' class.
Then is then hidden by standard ad blocking plugins. If you view source your field will be there.
Even though a field is marked as 'editable=False' in the model, I would like the admin page to display it. Currently it hides the field altogether.. How can this be achieved ?
Use Readonly Fields. Like so (for django >= 1.2):
class MyModelAdmin(admin.ModelAdmin):
readonly_fields=('first',)
Update
This solution is useful if you want to keep the field editable in Admin but non-editable everywhere else. If you want to keep the field non-editable throughout then #Till Backhaus' answer is the better option.
Original Answer
One way to do this would be to use a custom ModelForm in admin. This form can override the required field to make it editable. Thereby you retain editable=False everywhere else but Admin. For e.g. (tested with Django 1.2.3)
# models.py
class FooModel(models.Model):
first = models.CharField(max_length = 255, editable = False)
second = models.CharField(max_length = 255)
def __unicode__(self):
return "{0} {1}".format(self.first, self.second)
# admin.py
class CustomFooForm(forms.ModelForm):
first = forms.CharField()
class Meta:
model = FooModel
fields = ('second',)
class FooAdmin(admin.ModelAdmin):
form = CustomFooForm
admin.site.register(FooModel, FooAdmin)
Add the fields you want to display on your admin page.
Then add the fields you want to be read-only.
Your read-only fields must be in fields as well.
class MyModelAdmin(admin.ModelAdmin):
fields = ['title', 'author', 'published_date', 'updated_date', 'created_date']
readonly_fields = ('updated_date', 'created_date')
You could also set the readonly fields as editable=False in the model (django doc reference for editable here). And then in the Admin overriding the get_readonly_fields method.
# models.py
class MyModel(models.Model):
first = models.CharField(max_length=255, editable=False)
# admin.py
class MyModelAdmin(admin.ModelAdmin):
def get_readonly_fields(self, request, obj=None):
return [f.name for f in obj._meta.fields if not f.editable]
With the above solution I was able to display hidden fields for several objects but got an exception when trying to add a new object.
So I enhanced it like follows:
class HiddenFieldsAdmin(admin.ModelAdmin):
def get_readonly_fields(self, request, obj=None):
try:
return [f.name for f in obj._meta.fields if not f.editable]
except:
# if a new object is to be created the try clause will fail due to missing _meta.fields
return ""
And in the corresponding admin.py file I just had to import the new class and add it whenever registering a new model class
from django.contrib import admin
from .models import Example, HiddenFieldsAdmin
admin.site.register(Example, HiddenFieldsAdmin)
Now I can use it on every class with non-editable fields and so far I saw no unwanted side effects.
You can try this
#admin.register(AgentLinks)
class AgentLinksAdmin(admin.ModelAdmin):
readonly_fields = ('link', )
Django doesn't support getting foreign key values from list_display or list_filter (e.g foo__bar). I know you can create a module method as a workaround for list_display, but how would I go about to do the same for list_filter? Thanks.
Django supports list_filter with foreign key fields
# models.py:
class Foo(models.Model):
name = models.CharField(max_length=255)
def __unicode__(self):
return self.name
class Bar(models.Model):
name = models.CharField(max_length=255)
foo = models.ForeignKey(Foo)
# admin.py:
class BarAdmin(admin.ModelAdmin):
list_filter = ('foo__name')
From documentation: Field names in list_filter can also span relations using the __ lookup
Well, the docs say that you can may use ForeignKey field types in list_filter:
http://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_filter
An example:
# models.py:
class Foo(models.Model):
name = models.CharField(max_length=255)
def __unicode__(self):
return self.name
class Bar(models.Model):
name = models.CharField(max_length=255)
foo = models.ForeignKey(Foo)
# admin.py:
class BarAdmin(admin.ModelAdmin):
list_filter = ('foo')
If you want to filter by a field from the related model, there's a patch on the way to make this work (will probably be merged into 1.2 as it seems):
http://code.djangoproject.com/ticket/3400
solution from this page worked for me http://www.hoboes.com/Mimsy/hacks/fixing-django-124s-suspiciousoperation-filtering/
define
class SmarterModelAdmin(admin.ModelAdmin):
valid_lookups = ()
def lookup_allowed(self, lookup, *args, **kwargs):
if lookup.startswith(self.valid_lookups):
return True
return super(SmarterModelAdmin, self).lookup_allowed(lookup, *args, **kwargs)
then allow the lookup for certain foreign key field
class PageAdmin(SmarterModelAdmin):
valid_lookups = ('parent')
If you construct the URL for the changelist manually then Django has no problems following relationships. For example:
/admin/contact/contact/?participant__event=8
or
/admin/contact/contact/?participant__event__name__icontains=er
Both work fine (although the latter doesn't add 'distinct()' so might have duplicates but that won't usually be an issue for filters)
So you just need to add something to the page that creates the correct links. You can do this either with by overriding the changelist template or by writing a custom filterspec. There are several examples I found by Googling - particularly on Django Snippets
You can easily create custom filters since Django 1.4 by overriding django.contrib.admin.SimpleListFilter class.
More information :
Admin list_filter documentation ;
Django-1.4 release note.
I ran into the same problem and really needed a solution. I have a workaround that lets you create a filter on a FK related model property. You can even traverse more than one FK relationship. It creates a new FilterSpec subclass that subclasses the default RelatedFilterSpec used to give you a filter on a ForeignKey field.
See http://djangosnippets.org/snippets/2260/
The Haes answer works perfectly, but if the __ looks up to another ForeignKey field, you end up with a blank result. You must place another __ lookup, until it points to the real field.
In my case: list_filter = ('place__condo__name', )
my models.py:
class Condo(models.Model):
name = models.CharField(max_length=70)
...
class Place(models.Model):
condo = models.ForeignKey(Condo)
...
class Actions(models.Model):
place = models.ForeignKey(Place)
...