raw_id_fields and ManyToMany in Django admin - django

I want to use raw_id_fields on a ManyToMany relationship in the admin, and I want each related object to show up on its own row (as opposed to a comma-separated list in a single field, which is the default behavior). Following examples spotted in the wild, it seems like I should be able to do this:
# models.py
class Profile(models.Model):
...
follows = models.ManyToManyField(User,related_name='followees')
# admin.py
class FollowersInline(admin.TabularInline):
model = Profile
raw_id_fields = ('follows',)
extra = 1
class ProfileAdmin(admin.ModelAdmin):
search_fields = ('user__first_name','user__last_name','user__username',)
inlines = (FollowersInline,)
admin.site.register(Profile,ProfileAdmin)
But that generates the error:
<class 'bucket.models.Profile'> has no ForeignKey to <class 'bucket.models.Profile'>
I'm not clear what I'm doing wrong here. Thanks for suggestions.

Looks like you are setting the wrong model for your InlineAdmin
as the model for followers you are defining is User and not Profile.
Looking at the docs I'd say you should try:
class FollowersInline(admin.TabularInline):
model = Profile.follows.through
and
class ProfileAdmin(admin.ModelAdmin):
....
exclude = ('follows',)
inlines = (FollowersInline,)

In inline for m2m connection you should use through table and for raw_id_fields setting you should use fields from that through table - inside this table fields can be name different as you expect.
You need goes to sqlite3/psql etc. terminal to see through table schema and use propper field for raw_id_fields.
class FollowersInline(admin.TabularInline):
model = Profile.follows.through
# raw_id_fields = ("follows",) <- wrong
raw_id_fields = ("user",) # <- probably right, because your m2m relation with `User` table and django use name of that table to name field in `through` model

Related

Django-filter ordering by SerializerMethodField

My filter looks like this:
class CustomUserFilter(django_filters.FilterSet):
has_reviews_in_area = HasReviewsInArea(name='area__id')
has_reviews_in_venue = HasReviewsInVenue(name='venue__id')
class Meta:
model = CustomUser
fields = ['has_reviews_in_area', 'has_reviews_in_venue', 'access_level',]
order_by = ['id', '-id', 'average_rating']
The problem is that average_rating is a dynamic field created in my serializer like this:
average_rating = serializers.SerializerMethodField(source='average_rating')
Which is why I get an error that looks like:
FieldError at /app/users/
Cannot resolve keyword u'average_rating' into field.
Is there anyway I can tell django-filter to filter by my serializer field?
I am using djangorestframework 3.2.4 and django 1.8.4. (With ModelViewsets, ModelSerializers and FilterSets)
Also, I cannot make average_rating a property on my model because it is dynamically calculated according to my filters (e.g. average rating of venue in query parameter)

Enable sorting by __str__ column in django admin

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.

Django get all, with related models

Problem:
I'm using Django Rest Framework and i want to fetch all models with the relationships included, like this:
TestModel.objects.all()
My model looks like this:
class TestModel(models.Model):
name = models.CharField(max_length=32)
related_model = models.ForeignKey(TestRelation)
Problem is, i only get the Primary Keys for related_model but i need the whole related_model!
I'm using the ListCreateAPIView, with the above queryset (TestModel.objects.all()) and the most basic form of the ModelSerializer.
I tried the PrimaryKeyRelatedField but i get the same result..
Thanks!
Just create serializer for your related model:
class TestRelationSerializer(serializers.ModelSerializer):
class Meta:
meta = TestRelation
and use is as field in TestModelSerializer:
class TestModelSerializer(serializers.ModelSerializer):
related_model = TestRelationSerializer()
You can also do it other way around, by using TestModelSerializer as field in TestRelationSerializer with many set to true:
class TestRelationSerializer(serializers.ModelSerializer):
testmodel_set = TestModelSerializer(many=True)
just remember, you can't do both at once due to infinite recursion it makes.

Related lookup field foreign key doesn't work in inline Django

i've a problem with my tabularinline field. I have model like this
class Product(models.Model):
....
class Pemesanan(models.Model):
produks = models.ManyToManyField(Product, verbose_name=u"Kode Produk", through='Foo')
class Foo(models.Model):
product = models.ForeignKey(Product)
...
Class Foo is an intermediary class (manytomany field) with class Pemesanan and Class Product. It has a foreign key field to Class Pemesanan. Class Foo is displayed as an tabularinline in change_form template like this http://upload.ui.ac.id/?a=d&i=845380
But my problem is that field product doesn't show as a related lookup field as it is shown up as an ordinary form (not in inline). This is my Admin
class FooInline(admin.TabularInline):
model = Foo
extra = 0
allow_add = True
class PemesananAdmin(admin.ModelAdmin):
....
search_fields = ['produks']
raw_id_fields = ('produks',)
related_lookup_fields = {
'm2m': ['produks'],
}
inlines = [
FooInline,
]
exclude = ('produks',)
I have use autocomplete, but it seems so hard to implement here because the tutorial is incomplete. So is there a way for me to get my related lookup works in my tabularinline ? Thank you very much :D.
So, yeah, I think I did simply misunderstand what you were asking. You're just wanting the related lookup popup to select a product in each inline, rather than a select box. You already know about raw_id_fields; the problem is that you need to specify that on the inline model admin, not the main parent model admin.
class FooInline(admin.TabularInline):
model = Foo
extra = 0
allow_add = True
raw_id_fields = ('product',)

Django: list_filter and foreign key fields

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