mirror of
https://github.com/jazzband/django-eav2.git
synced 2026-05-03 21:24:49 +00:00
commit
e046c54a86
8 changed files with 154 additions and 14 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -5,3 +5,4 @@
|
|||
*.sqlite*
|
||||
_build
|
||||
build
|
||||
django_eav.egg-info/*
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
# vim: ai ts=4 sts=4 et sw=4 coding=utf-8
|
||||
#
|
||||
# This software is derived from EAV-Django originally written and
|
||||
# This software is derived from EAV-Django originally written and
|
||||
# copyrighted by Andrey Mikhaylenko <http://pypi.python.org/pypi/eav-django>
|
||||
#
|
||||
# This is free software: you can redistribute it and/or modify
|
||||
|
|
@ -28,7 +28,7 @@ from django.utils.safestring import mark_safe
|
|||
from .models import Attribute, Value, EnumValue, EnumGroup
|
||||
|
||||
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
|
||||
|
|
@ -94,7 +94,7 @@ class BaseEntityInline(InlineModelAdmin):
|
|||
return [(None, {'fields': form.fields.keys()})]
|
||||
|
||||
class AttributeAdmin(ModelAdmin):
|
||||
list_display = ('name', 'slug', 'datatype', 'description', 'site')
|
||||
list_display = ('name', 'content_type', 'slug', 'datatype', 'description', 'site')
|
||||
list_filter = ['site']
|
||||
prepopulated_fields = {'slug': ('name',)}
|
||||
|
||||
|
|
|
|||
|
|
@ -82,9 +82,10 @@ class EavDatatypeField(models.CharField):
|
|||
:class:`~eav.models.Value` objects.
|
||||
'''
|
||||
super(EavDatatypeField, self).validate(value, instance)
|
||||
from .models import Attribute
|
||||
if not instance.pk:
|
||||
return
|
||||
if type(instance).objects.get(pk=instance.pk).datatype == instance.datatype:
|
||||
return
|
||||
if instance.value_set.count():
|
||||
raise ValidationError(_(u"You cannot change the datatype of an "
|
||||
u"attribute that is already in use."))
|
||||
|
|
|
|||
|
|
@ -110,11 +110,11 @@ def expand_eav_filter(model_cls, key, value):
|
|||
return '%s__in' % gr_name, value
|
||||
|
||||
try:
|
||||
field, m, direct, m2m = model_cls._meta.get_field_by_name(fields[0])
|
||||
field = model_cls._meta.get_field(fields[0])
|
||||
except models.FieldDoesNotExist:
|
||||
return key, value
|
||||
|
||||
if direct:
|
||||
if not field.auto_created or field.concrete:
|
||||
return key, value
|
||||
else:
|
||||
sub_key = '__'.join(fields[1:])
|
||||
|
|
|
|||
96
eav/migrations/0001_initial.py
Normal file
96
eav/migrations/0001_initial.py
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.10 on 2016-10-13 05:56
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import django.contrib.sites.managers
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.db.models.manager
|
||||
import django.utils.timezone
|
||||
import eav.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('contenttypes', '0002_remove_content_type_name'),
|
||||
('sites', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Attribute',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(help_text='User-friendly attribute name', max_length=100, verbose_name='name')),
|
||||
('slug', eav.fields.EavSlugField(help_text='Short unique attribute label', verbose_name='slug')),
|
||||
('description', models.CharField(blank=True, help_text='Short description', max_length=256, null=True, verbose_name='description')),
|
||||
('type', models.CharField(blank=True, max_length=20, null=True, verbose_name='type')),
|
||||
('datatype', eav.fields.EavDatatypeField(choices=[(b'text', 'Text'), (b'float', 'Float'), (b'int', 'Integer'), (b'date', 'Date'), (b'bool', 'True / False'), (b'object', 'Django Object'), (b'enum', 'Multiple Choice')], max_length=6, verbose_name='data type')),
|
||||
('created', models.DateTimeField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
|
||||
('modified', models.DateTimeField(auto_now=True, verbose_name='modified')),
|
||||
('required', models.BooleanField(default=False, verbose_name='required')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['name'],
|
||||
},
|
||||
managers=[
|
||||
('objects', django.db.models.manager.Manager()),
|
||||
('on_site', django.contrib.sites.managers.CurrentSiteManager()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='EnumGroup',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=100, unique=True, verbose_name='name')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='EnumValue',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('value', models.CharField(db_index=True, max_length=50, unique=True, verbose_name='value')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Value',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('entity_id', models.IntegerField()),
|
||||
('value_text', models.TextField(blank=True, null=True)),
|
||||
('value_float', models.FloatField(blank=True, null=True)),
|
||||
('value_int', models.IntegerField(blank=True, null=True)),
|
||||
('value_date', models.DateTimeField(blank=True, null=True)),
|
||||
('value_bool', models.NullBooleanField()),
|
||||
('generic_value_id', models.IntegerField(blank=True, null=True)),
|
||||
('created', models.DateTimeField(default=django.utils.timezone.now, verbose_name='created')),
|
||||
('modified', models.DateTimeField(auto_now=True, verbose_name='modified')),
|
||||
('attribute', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='eav.Attribute', verbose_name='attribute')),
|
||||
('entity_ct', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='value_entities', to='contenttypes.ContentType')),
|
||||
('generic_value_ct', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='value_values', to='contenttypes.ContentType')),
|
||||
('value_enum', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='eav_values', to='eav.EnumValue')),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='enumgroup',
|
||||
name='enums',
|
||||
field=models.ManyToManyField(to='eav.EnumValue', verbose_name='enum group'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='attribute',
|
||||
name='enum_group',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='eav.EnumGroup', verbose_name='choice group'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='attribute',
|
||||
name='site',
|
||||
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='sites.Site', verbose_name='site'),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='attribute',
|
||||
unique_together=set([('site', 'slug')]),
|
||||
),
|
||||
]
|
||||
35
eav/migrations/0002_auto_20161014_0157.py
Normal file
35
eav/migrations/0002_auto_20161014_0157.py
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.10 on 2016-10-13 16:57
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contenttypes', '0002_remove_content_type_name'),
|
||||
('eav', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='attribute',
|
||||
options={'ordering': ['content_type', 'name']},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='attribute',
|
||||
name='content_type',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType', verbose_name='content type'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='attribute',
|
||||
name='display_order',
|
||||
field=models.PositiveIntegerField(default=1, verbose_name='display order'),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='attribute',
|
||||
unique_together=set([('site', 'content_type', 'slug')]),
|
||||
),
|
||||
]
|
||||
0
eav/migrations/__init__.py
Normal file
0
eav/migrations/__init__.py
Normal file
|
|
@ -152,8 +152,8 @@ class Attribute(models.Model):
|
|||
'''
|
||||
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
unique_together = ('site', 'slug')
|
||||
ordering = ['content_type', 'name']
|
||||
unique_together = ('site', 'content_type', 'slug')
|
||||
|
||||
TYPE_TEXT = 'text'
|
||||
TYPE_FLOAT = 'float'
|
||||
|
|
@ -176,6 +176,10 @@ class Attribute(models.Model):
|
|||
name = models.CharField(_(u"name"), max_length=100,
|
||||
help_text=_(u"User-friendly attribute name"))
|
||||
|
||||
content_type = models.ForeignKey(ContentType,
|
||||
blank=True, null=True,
|
||||
verbose_name=_(u"content type"))
|
||||
|
||||
site = models.ForeignKey(Site, verbose_name=_(u"site"),
|
||||
default=settings.SITE_ID)
|
||||
|
||||
|
|
@ -205,6 +209,8 @@ class Attribute(models.Model):
|
|||
|
||||
required = models.BooleanField(_(u"required"), default=False)
|
||||
|
||||
display_order = models.PositiveIntegerField(_(u"display order"), default=1)
|
||||
|
||||
objects = models.Manager()
|
||||
on_site = CurrentSiteManager()
|
||||
|
||||
|
|
@ -312,7 +318,7 @@ class Attribute(models.Model):
|
|||
value_obj.save()
|
||||
|
||||
def __unicode__(self):
|
||||
return u"%s (%s)" % (self.name, self.get_datatype_display())
|
||||
return u"%s.%s (%s)" % (self.content_type, self.name, self.get_datatype_display())
|
||||
|
||||
|
||||
class Value(models.Model):
|
||||
|
|
@ -442,18 +448,19 @@ class Entity(object):
|
|||
Return a query set of all :class:`Attribute` objects that can be set
|
||||
for this entity.
|
||||
'''
|
||||
return self.model._eav_config_cls.get_attributes()
|
||||
return self.model._eav_config_cls.get_attributes().filter(
|
||||
models.Q(content_type__isnull=True) | models.Q(content_type=self.ct)).order_by('display_order')
|
||||
|
||||
def _hasattr(self, attribute_slug):
|
||||
'''
|
||||
Since we override __getattr__ with a backdown to the database, this exists as a way of
|
||||
Since we override __getattr__ with a backdown to the database, this exists as a way of
|
||||
checking whether a user has set a real attribute on ourselves, without going to the db if not
|
||||
'''
|
||||
return attribute_slug in self.__dict__
|
||||
|
||||
def _getattr(self, attribute_slug):
|
||||
'''
|
||||
Since we override __getattr__ with a backdown to the database, this exists as a way of
|
||||
Since we override __getattr__ with a backdown to the database, this exists as a way of
|
||||
getting the value a user set for one of our attributes, without going to the db to check
|
||||
'''
|
||||
return self.__dict__[attribute_slug]
|
||||
|
|
@ -482,7 +489,7 @@ class Entity(object):
|
|||
value = self._getattr(attribute.slug)
|
||||
else:
|
||||
value = values_dict.get(attribute.slug, None)
|
||||
|
||||
|
||||
if value is None:
|
||||
if attribute.required:
|
||||
raise ValidationError(_(u"%(attr)s EAV field cannot " \
|
||||
|
|
@ -495,7 +502,7 @@ class Entity(object):
|
|||
raise ValidationError(_(u"%(attr)s EAV field %(err)s") % \
|
||||
{'attr': attribute.slug,
|
||||
'err': e})
|
||||
|
||||
|
||||
def get_values_dict(self):
|
||||
values_dict = dict()
|
||||
for value in self.get_values():
|
||||
|
|
|
|||
Loading…
Reference in a new issue