Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master' into feature/add-uuid-…
Browse files Browse the repository at this point in the history
…attribute

# Conflicts:
#	.gitignore
#	eav/models.py
#	eav/validators.py
  • Loading branch information
musamusa committed May 3, 2018
2 parents 233b2df + 41e99fa commit 5e1c98e
Show file tree
Hide file tree
Showing 12 changed files with 97 additions and 19 deletions.
26 changes: 24 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,29 @@ You can install django-eav directly from guthub::

pip install -e git+git://github.com/mvpdev/django-eav.git#egg=django-eav

Prerequisites
-------------

Django Sites Framework
~~~~~~~~~~~~~~~~~~~~~~
As of Django 1.7, the `Sites framework <https://docs.djangoproject.com/en/1.8/ref/contrib/sites/#enabling-the-sites-framework>`_ is not enabled by default; Django-EAV requires this framework.
To enable the sites framework, follow these steps:

Add ``django.contrib.sites`` to your INSTALLED_APPS setting. Be sure to add sites to the installed apps list BEFORE eav!

Define a ``SITE_ID`` setting::

SITE_ID = 1

Run ``migrate``


Usage
-----

Edit settings.py
~~~~~~~~~~~~~~~~
Add ``eav`` to your ``INSTALLED_APPS`` in your project's ``settings.py`` file.
Add ``eav`` to your ``INSTALLED_APPS`` in your project's ``settings.py`` file. Be sure to add eav to the installed apps list AFTER the sites framework!

Register your model(s)
~~~~~~~~~~~~~~~~~~~~~~
Expand All @@ -52,7 +69,12 @@ model with eav::
>>> eav.register(MyModel)

Generally you would do this in your ``models.py`` immediate after your model
declarations.
declarations. Alternatively, you can use the registration decorator provided::

from eav.decorators import register_eav
@register_eav()
class MyModel(models.Model):
...

Create some attributes
~~~~~~~~~~~~~~~~~~~~~~
Expand Down
4 changes: 2 additions & 2 deletions eav/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def render_change_form(self, request, context, add=False, change=False, form_url
form = context['adminform'].form

# infer correct data from the form
fieldsets = self.fieldsets or [(None, {'fields': form.fields.keys()})]
fieldsets = self.fieldsets or [(None, {'fields': list(form.fields.keys())})]
adminform = admin.helpers.AdminForm(form, fieldsets,
self.prepopulated_fields)
media = mark_safe(self.media + adminform.media)
Expand Down Expand Up @@ -92,7 +92,7 @@ def get_fieldsets(self, request, obj=None):
instance = self.model(**kw)
form = formset.form(request.POST, instance=instance)

return [(None, {'fields': form.fields.keys()})]
return [(None, {'fields': list(form.fields.keys())})]


class AttributeAdmin(ModelAdmin):
Expand Down
19 changes: 19 additions & 0 deletions eav/decorators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
def register_eav(**kwargs):
"""
Registers the given model(s) classes and wrapped Model class with
django-eav:
@register_eav
class Author(models.Model):
pass
"""
from . import register
from django.db.models import Model

def _model_eav_wrapper(model_class):
if not issubclass(model_class, Model):
raise ValueError('Wrapped class must subclass Model.')
register(model_class, **kwargs)
return model_class

return _model_eav_wrapper
2 changes: 1 addition & 1 deletion eav/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def _build_dynamic_fields(self):
self.fields[attribute.slug] = MappedField(**defaults)

# fill initial data (if attribute was already defined)
if value and not datatype == attribute.TYPE_ENUM: #enum done above
if not value is None and not datatype == attribute.TYPE_ENUM: #enum done above
self.initial[attribute.slug] = value

def save(self, commit=True):
Expand Down
8 changes: 5 additions & 3 deletions eav/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
Functions and Classes
---------------------
'''
import six

from functools import wraps

from django.db import models
Expand All @@ -49,7 +51,7 @@ def wrapper(self, *args, **kwargs):
new_args.append(arg)

new_kwargs = {}
for key, value in kwargs.items():
for key, value in list(kwargs.items()):
# modify kwargs (warning: recursion ahead)
new_key, new_value = expand_eav_filter(self.model, key, value)
new_kwargs.update({new_key: new_value})
Expand Down Expand Up @@ -166,15 +168,15 @@ def create(self, **kwargs):

new_kwargs = {}
eav_kwargs = {}
for key, value in kwargs.iteritems():
for key, value in six.iteritems(kwargs):
if key.startswith(prefix):
eav_kwargs.update({key[len(prefix):]: value})
else:
new_kwargs.update({key: value})

obj = self.model(**new_kwargs)
obj_eav = getattr(obj, config_cls.eav_attr)
for key, value in eav_kwargs.iteritems():
for key, value in six.iteritems(eav_kwargs):
setattr(obj_eav, key, value)
obj.save()
return obj
Expand Down
17 changes: 12 additions & 5 deletions eav/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
Classes
-------
'''
from __future__ import unicode_literals
from builtins import object


from django.utils import timezone
Expand All @@ -43,6 +45,7 @@
from django.contrib.sites.models import Site
from django.contrib.sites.managers import CurrentSiteManager
from django.conf import settings
from django.utils.encoding import python_2_unicode_compatible

from .validators import *
from .fields import EavSlugField, EavDatatypeField
Expand Down Expand Up @@ -81,7 +84,8 @@ class EnumValue(models.Model):
value = models.CharField(_(u"value"), db_index=True,
unique=True, max_length=50)

def __unicode__(self):
@python_2_unicode_compatible
def __str__(self):
return self.value


Expand All @@ -98,7 +102,8 @@ class EnumGroup(models.Model):

enums = models.ManyToManyField(EnumValue, verbose_name=_(u"enum group"))

def __unicode__(self):
@python_2_unicode_compatible
def __str__(self):
return self.name


Expand Down Expand Up @@ -152,7 +157,7 @@ class Attribute(models.Model):
change it's datatype.
'''

class Meta:
class Meta(object):
ordering = ['content_type', 'name']
unique_together = ('site', 'content_type', 'slug')

Expand Down Expand Up @@ -317,7 +322,8 @@ def save_value(self, entity, value):
value_obj.value = value
value_obj.save()

def __unicode__(self):
@python_2_unicode_compatible
def __str__(self):
return u"%s.%s (%s)" % (self.content_type, self.name, self.get_datatype_display())


Expand Down Expand Up @@ -403,7 +409,8 @@ def _set_value(self, new_value):

value = property(_get_value, _set_value)

def __unicode__(self):
@python_2_unicode_compatible
def __str__(self):
return u"%s - %s: \"%s\"" % (self.entity, self.attribute.name,
self.value)

Expand Down
1 change: 1 addition & 0 deletions eav/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
Classes
-------
'''
from builtins import object

from django.db.utils import DatabaseError
from django.db.models.signals import pre_init, post_init, pre_save, post_save
Expand Down
3 changes: 2 additions & 1 deletion eav/tests/misc_models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from builtins import str
from django.test import TestCase

from ..models import EnumGroup, Attribute, Value
Expand All @@ -11,7 +12,7 @@ class MiscModels(TestCase):
def test_enumgroup_unicode(self):
name = 'Yes / No'
e = EnumGroup.objects.create(name=name)
self.assertEqual(unicode(e), name)
self.assertEqual(str(e), name)

def test_attribute_help_text(self):
desc = 'Patient Age'
Expand Down
15 changes: 13 additions & 2 deletions eav/tests/models.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from builtins import object
from django.db import models
from ..decorators import register_eav

class Patient(models.Model):
class Meta:
class Meta(object):
app_label = 'eav'

name = models.CharField(max_length=12)
Expand All @@ -10,7 +12,7 @@ def __unicode__(self):
return self.name

class Encounter(models.Model):
class Meta:
class Meta(object):
app_label = 'eav'

num = models.PositiveSmallIntegerField()
Expand All @@ -19,3 +21,12 @@ class Meta:
def __unicode__(self):
return '%s: encounter num %d' % (self.patient, self.num)

@register_eav()
class ExampleModel(models.Model):
class Meta(object):
app_label = 'eav'

name = models.CharField(max_length=12)

def __unicode__(self):
return self.name
13 changes: 12 additions & 1 deletion eav/tests/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from ..managers import EntityManager
from ..models import Attribute

from .models import Patient, Encounter
from .models import Patient, Encounter, ExampleModel


class RegistryTests(TestCase):
Expand Down Expand Up @@ -56,6 +56,11 @@ def test_registering_overriding_defaults(self):
eav.unregister(Patient)
eav.unregister(Encounter)

def test_registering_via_decorator_with_defaults(self):
self.assertTrue(hasattr(ExampleModel, '_eav_config_cls'))
self.assertEqual(ExampleModel._eav_config_cls.manager_attr, 'objects')
self.assertEqual(ExampleModel._eav_config_cls.eav_attr, 'eav')

def test_unregistering(self):
old_mgr = Patient.objects
eav.register(Patient)
Expand All @@ -65,6 +70,12 @@ def test_unregistering(self):
self.assertEqual(Patient.objects, old_mgr)
self.assertFalse(hasattr(Patient, '_eav_config_cls'))

def test_unregistering_via_decorator(self):
self.assertTrue(ExampleModel.objects.__class__.__name__ == 'EntityManager')
eav.unregister(ExampleModel)
e = ExampleModel()
self.assertFalse(ExampleModel.objects.__class__.__name__ == 'EntityManager')

def test_unregistering_unregistered_model_proceeds_silently(self):
eav.unregister(Patient)

Expand Down
6 changes: 5 additions & 1 deletion eav/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def validate_text(value):
'''
Raises ``ValidationError`` unless *value* type is ``str`` or ``unicode``
'''
if not (type(value) == str):
if not (type(value) == str or type(value) == str):
raise ValidationError(_(u"Must be str or unicode"))


Expand Down Expand Up @@ -101,8 +101,12 @@ def validate_enum(value):
Raises ``ValidationError`` unless *value* is a saved
:class:`~eav.models.EnumValue` model instance.
'''
pass
"""
# This never passes, value is a str
from .models import EnumValue
if not isinstance(value, EnumValue):
raise ValidationError(_(u"Must be an EnumValue model object instance"))
if not value.pk:
raise ValidationError(_(u"EnumValue has not been saved yet"))
"""
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
version=__import__('eav').__version__,
license = 'GNU Lesser General Public License (LGPL), Version 3',

requires = ['python (>= 2.5)', 'django (>= 1.2)'],
requires = ['python (>= 2.5)', 'django (>= 1.2)', 'six'],
provides = ['eav'],

description='Entity-attribute-value model implementation as a reusable'
Expand Down

0 comments on commit 5e1c98e

Please sign in to comment.