views:

807

answers:

1

good day guys! in project, among others, have models:

class Category(models.Model):

    name = models.CharField(max_length = 50, blank = False, null = False)

    def __unicode__(self):
        return "Category %s" % self.name

    class Meta:
        db_table = "categories"
        managed = False


class Site(models.Model):

    user = models.ForeignKey(User, blank = False, null = False, db_column = "user_id")
    name = models.URLField(verify_exists = True, blank = False, null = False)
    categories = models.ManyToManyField(Category, blank = True, through = "CategorySites", verbose_name = "Category")

    def __unicode__(self):
        return self.name

    class Meta:
        db_table = "sites"
        managed = False



class CategorySites(models.Model):

    site = models.ForeignKey(Site, blank = False, null = False, db_column = "site_id")
    category = models.ForeignKey(Category, blank = False, null = False, db_column = "category_id")

    def __unicode__(self):
        return "Relation between site %s and category %s" % (self.site.name, self.category.name)

    class Meta:
        db_table = "categories_sites"
        managed = False

so, as you see there is many-to-many relation. generally it work well - i can add and manage models through manage.py shell, or server-side functions. I want to enable editing this type of relation on admin site, so, i've added admin model for Sites:

 class SiteAdmin(admin.ModelAdmin):

    list_display = ('id', 'name')
    list_filter = ('name', 'categories')

    fieldsets = (
        (None, {"fields": ("categories",)}),
    )

    def queryset(self, request):
        qs = super(SiteAdmin, self).queryset(request)
        if request.user.is_superuser:
            return qs
        else:
            return qs.filter(user = request.user)

    def has_change_permission(self, request, obj=None):
        if not obj:
            return True # So they can see the change list page
        if request.user.is_superuser or obj.user == request.user:
            return True
        else:
            return False

    has_delete_permission = has_change_permission

but when i'm going in admin->sites->Add site (or edit) django throws Caught an exception while rendering: 'NoneType' object has no attribute 'label' . how can if fix it?

+1  A: 

For that you need to add undocumented formfield_for_manytomany method to your SiteAdmin class:

from django.contrib.admin import widgets

class SitebAdmin(admin.ModelAdmin):

   list_display = ('id', 'name')
   list_filter = ('name', 'categories')

   def formfield_for_manytomany(self, db_field, request, **kwargs):
      if db_field.name == 'categories':
         kwargs['widget'] = widgets.FilteredSelectMultiple(
              db_field.verbose_name, (db_field.name in self.filter_vertical))
      return super(SitebAdmin, self).formfield_for_foreignkey(
         db_field, request, **kwargs)

   fieldsets = (
       (None, {
            "fields": ("name", "categories",)
       }),
   )

to override default of not displaying multiple select widget for models with through option specified docs.

Although this works I still think that this should not cause error in Django.

Łukasz
Lukasz, thanx for your answer - it really helps, but with category sites as inline, in admin interface it looks like one combobox with choices from categories, after saving with some category - new combobox appears and so on. i'd like to have a field, like, in users<->groups, or user<->permissions (ability to select few categories at one time, and assign all selected)
Anthony Koval'
thanks again. but it works only in "one direction" i see the correct widget, with two columns, and ability to bulk add/delete categories, but on save it throws error, like: <-- Cannot set values on a ManyToManyField which specifies an intermediary model. Use CategorySites's Manager instead. --> and he is totally right - i'm using intermediary table :), any chance to override this behavior (handle category saving function by myself)?
Anthony Koval'
You're right. It looks like there's no easy solution to this problem. Many to many associations with `through` models are not supported as well as one would imagine. I don't know what requirement forces you to choose `through` solution, but maybe there's better way (better suited for Django) to handle that.
Łukasz