django-eav2/eav/admin.py
Iwo Herka 4958bfd04b
Remove Sites framework dependency (#9) (#15)
This merge does two things:

    1. Removes completely dependency on the Sites framework (which was implemented erroneously).
    Specifically, site field on Attribute and related constraints. With those changes, attributes are now global. That is, all attributes are visible to all entities. As a result, slug in now unique.

    2. Content-type field is used to limit entities that can be assigned an attribute (see get_all_attributes method on Entity). This way attributes with content_type assigned are, for all intended purposes, private to Entity of that content type. The problem is that those attributes are still visible to other entities. Moreover, originally, attributes had non-unique slugs that are unique together with content_type and site. With removal of site (and because content_type is nullable) a situation might exist in which two attributes exist with the same slug/name but one with content_type and one with NULL. Simple solution to all this hassle is to drop private attributes altogether, which is what this pull-request does.

Resolves: #9

Commits:
* Remove sites dependency; fix Attribute uniqueness (#9)
* Add uniqueness test to attributes
* Remove content_type from Attribute class
* Update runtests
* Remove redudant  call
* Fix wrong assertion in attribute test
* Fix string formatting
2018-06-04 17:19:05 +02:00

85 lines
3.2 KiB
Python

'''Admin. This module contains classes used for admin integration.'''
from django.contrib import admin
from django.contrib.admin.options import (InlineModelAdmin, ModelAdmin,
StackedInline)
from django.forms.models import BaseInlineFormSet
from django.utils.safestring import mark_safe
from .models import Attribute, EnumGroup, EnumValue, Value
class BaseEntityAdmin(ModelAdmin):
def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
"""
Wrapper for ModelAdmin.render_change_form. Replaces standard static
AdminForm with an EAV-friendly one. The point is that our form generates
fields dynamically and fieldsets must be inferred from a prepared and
validated form instance, not just the form class. Django does not seem
to provide hooks for this purpose, so we simply wrap the view and
substitute some data.
"""
form = context['adminform'].form
# infer correct data from the form
fieldsets = self.fieldsets or [(None, {'fields': form.fields.keys()})]
adminform = admin.helpers.AdminForm(form, fieldsets,
self.prepopulated_fields)
media = mark_safe(self.media + adminform.media)
context.update(adminform=adminform, media=media)
super_meth = super(BaseEntityAdmin, self).render_change_form
return super_meth(request, context, add, change, form_url, obj)
class BaseEntityInlineFormSet(BaseInlineFormSet):
"""
An inline formset that correctly initializes EAV forms.
"""
def add_fields(self, form, index):
if self.instance:
setattr(form.instance, self.fk.name, self.instance)
form._build_dynamic_fields()
super(BaseEntityInlineFormSet, self).add_fields(form, index)
class BaseEntityInline(InlineModelAdmin):
"""
Inline model admin that works correctly with EAV attributes. You should mix
in the standard StackedInline or TabularInline classes in order to define
formset representation, e.g.::
class ItemInline(BaseEntityInline, StackedInline):
model = Item
form = forms.ItemForm
.. warning: TabularInline does *not* work out of the box. There is,
however, a patched template `admin/edit_inline/tabular.html` bundled
with EAV-Django. You can copy or symlink the `admin` directory to your
templates search path (see Django documentation).
"""
formset = BaseEntityInlineFormSet
def get_fieldsets(self, request, obj=None):
if self.declared_fieldsets:
return self.declared_fieldsets
formset = self.get_formset(request)
fk_name = self.fk_name or formset.fk.name
kw = {fk_name: obj} if obj else {}
instance = self.model(**kw)
form = formset.form(request.POST, instance=instance)
return [(None, {'fields': form.fields.keys()})]
class AttributeAdmin(ModelAdmin):
list_display = ('name', 'slug', 'datatype', 'description')
prepopulated_fields = {'slug': ('name',)}
admin.site.register(Attribute, AttributeAdmin)
admin.site.register(Value)
admin.site.register(EnumValue)
admin.site.register(EnumGroup)