Coder Social home page Coder Social logo

django-model-utils's Introduction

django-model-utils's People

Contributors

adamchainz avatar angrylawyer avatar asday avatar auvipy avatar carljm avatar charettes avatar dmeehan avatar dmytrokyrychuk avatar foarsitter avatar folz avatar funkybob avatar hramezani avatar hugovk avatar jarekwg avatar jayvdb avatar jezdez avatar joeriddles avatar joshuadavidthomas avatar kezabelle avatar lucaswiman avatar marfyl avatar mthuurne avatar phist0ne avatar pre-commit-ci[bot] avatar rdil avatar romgar avatar schinckel avatar tony avatar treyhunner avatar tumb1er avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

django-model-utils's Issues

select_sublcasses does not work with prefetch_related

Consider having the following example models:

from django.db import models
from model_utils.managers import InheritanceManager

class Post(models.Model):
    creation_date = models.DateTimeField(auto_now_add=True)
    objects = InheritanceManager()

class TextPost(Post):
    body = models.TextField()

class Photo(models.Model):
    image = models.ImageField(upload_to='photos')

class PhotoPost(Post):
    photos = models.ManyToManyField(Photo)

When I attempt to select all posts and prefetch PhotoPost's photos:

Post.objects.select_subclasses().prefetch_related('photos').all()

The following error is raised:

    (0.001) SELECT `photos_post`.`id`, `photos_post`.`creation_date`, `photos_textpost`.`post_ptr_id`, `photos_textpost`.`body`, `photos_photopost`.`post_ptr_id` FROM `photos_post` LEFT OUTER JOIN `photos_textpost` ON (`photos_post`.`id` = `photos_textpost`.`post_ptr_id`) LEFT OUTER JOIN `photos_photopost` ON (`photos_post`.`id` = `photos_photopost`.`post_ptr_id`) LIMIT 21; args=()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/Users/anislav/.virtualenvs/mysite/lib/python2.7/site-packages/django/db/models/query.py", line 77, in __repr__
    data = list(self[:REPR_OUTPUT_SIZE + 1])
  File "/Users/anislav/.virtualenvs/mysite/lib/python2.7/site-packages/django/db/models/query.py", line 102, in __iter__
    len(self)
  File "/Users/anislav/.virtualenvs/mysite/lib/python2.7/site-packages/django/db/models/query.py", line 94, in __len__
    self._prefetch_related_objects()
  File "/Users/anislav/.virtualenvs/mysite/lib/python2.7/site-packages/django/db/models/query.py", line 613, in _prefetch_related_objects
    prefetch_related_objects(self._result_cache, self._prefetch_related_lookups)
  File "/Users/anislav/.virtualenvs/mysite/lib/python2.7/site-packages/django/db/models/query.py", line 1742, in prefetch_related_objects
    (attr, first_obj.__class__.__name__, lookup))
AttributeError: Cannot find 'photos' on TextPost object, 'photos' is an invalid parameter to prefetch_related()

Is prefetch_related supported in combination with select_sublcasses?
Note that I have tested this on Django 1.5 and 1.6 with the latest release of django-model-utils.

Disclaimer: the given example is reworked version of the following one: http://stackoverflow.com/questions/19511368/prefetch-related-with-django-1-5-django-model-utils

ContentType filtering

Since InheritanceCastModel was deprecated, we can't filter on content types or i don't know how...

Is there a new way to make a filter like below when using InheritanceCastModel ?

SomeInheritanceCastModel.objects.filter(real_type__in=[...])

If not, it would be useful to remove deprecated on InheritanceCastModel and use it in conjunction with InheritanceManager

Add deferred model support

When I use .only() queryset method and there is more than one subclass I get DoesNotExist exception. It is raised in

obj = [getattr(obj, s) for s in self.subclasses if getattr(obj, s)] or [obj]

InheritanceManager breaks when manually specifying the OneToOne link

Assume the following model:

class Transaction(TimeStampedModel):
    description = models.TextField(null=False, default='', blank=True)

class BitcoinPaymentTransaction(Transaction):
    transaction_ptr = models.OneToOneField(Transaction, related_name='bitcoin_payment', parent_link=True)

    coinbase_id = models.CharField(null=False, blank=False, max_length=250)

The transaction_ptr OneToOne field is used so that when working with a Transaction object I could use tx.bitcoin_payment versus the uglier tx.bitcoinpaymenttransaction. Unfortunately this breaks the InheritanceManager because the code in _get_subclasses_recurse looks at rel.var_name. I tried replacing var_name with rel.get_accessor_name() and it seemed to fix the issue but am not sure about possible side effects. Tests do run fine after replacing var_name with get_accessor_name() so it might be the way to go

PassThroughManager break Django's RelatedManager behaviour

Hi, I would like to report a bug that I found on our projects which is django-model_utils related

  • What happened
    On this commit f7f54a0 model-utils' PassThroughManager breaks the behaviour of Django's related manager
  • How to reproduce
    on model_utils/tests/tests.py line 611

Change this

Spot.objects.create(name='The Crib', owner=self.dude, closed=True, secure=True)

Into this

self.dude.spots_owned.create(name='The Crib', closed=True, secure=True)

It will then throw this error

Traceback (most recent call last):
File "/Users/....../dev/django-model-utils/model_utils/tests/tests.py", line 613, in setUp
self.dude.spots_owned.create(name='The Crib', owner=self.dude, closed=True, secure=True)
File "/Library/Python/2.7/site-packages/django/db/models/fields/related.py", line 485, in create
return super(RelatedManager, self.db_manager(db)).create(**kwargs)
TypeError: super(type, obj): obj must be an instance or subtype of type
  • What should be expected

It should not return error. It behaves normally before this particular commit.

PassThroughManager on related fields drops relation.

When you use a PassThroughManager on a model, and then have a related field, fetching the related objects drops the filter on the relation.

Thus:

foo = Foo.objects.get(pk=1)
foo.bar_set.all().query → does not contain the clause restricting bar objects to foo_id=1

FieldTracker isn't behaving as expected for OneToOneField

I have to models with a OneToOneField relationship between them. When I try to query changed() or has_changed() for that field after assigning a value to the OneToOne but before saving, both functions return False for the field. It appears what is happening is that the FieldTracker is looks at the [field]_id attribute but that attribute doesn't get updated until the model is saved. This is for version 1.5.0.

See below for example. Please excuse any typos.

from django.db import models
from model_utils import FieldTracker

class Model1(models.Model):
    pass


class Model2(models.Model):
    model1 = models.OneToOneField(Model1, null=True)
    tracker = FieldTracker(fields=["model1"])


m1 = Model1().save()
m2 = Model2().save()

m2.model1 = m1
m2.tracker.changed() # This is showing {}
m2.tracker.has_changed("model1") # This is evaluating to False

Can't use PassThroughManager with a subclass of GeoQuerySet and InheritanceQuerySet

I am sorry to post this as an issue because it is more of a support question (I think).

My understanding of PassThroughManager is that I should be able to mix two QuerySet-derived classes into one. For example, I'd like to have a Manager that uses InheritanceManager and GeoManager, because my models inherit from a common concrete model and have geospatial data.

The only way I can make it work the way I want is by changing InheritanceQuerySet to inherit from GeoQuerySet instead of the vanilla QuerySet. Is that the correct way to do this? I feel like I shouldn't be modifying code django-model-utils. The old "from_manager" function seems closer to what I need, but it's been removed from model-utils.

AssertionError: To use StatusField, the model 'MyModel' must have a STATUS choices class attribute.

Hello Carl, I found a possible issue, maybe in Django it self, but that we should handle here.

On StatusField we have a contribute_to_class method, that set choices as cls.STATUS.

On Django it is called at https://github.com/django/django/blob/1.4.5/django/db/models/base.py#L217

def add_to_class(cls, name, value):
    if hasattr(value, 'contribute_to_class'):
        value.contribute_to_class(cls, name)
    else:
        setattr(cls, name, value)

This 'add_to_class' is called at https://github.com/django/django/blob/1.4.5/django/db/models/base.py#L98 in this case:

# Add all attributes to the class.
for obj_name, obj in attrs.items():
    new_class.add_to_class(obj_name, obj)

Ok, and your method is at https://github.com/carljm/django-model-utils/blob/master/model_utils/fields.py#L57:

def contribute_to_class(self, cls, name):
    if not cls._meta.abstract and self.check_for_status:
        assert hasattr(cls, 'STATUS'), \
            "To use StatusField, the model '%s' must have a STATUS choices class attribute." \
            % cls.__name__
        self._choices = cls.STATUS
        if not self.has_default():
            self.default = tuple(cls.STATUS)[0][0]  # set first as default
    super(StatusField, self).contribute_to_class(cls, name

Well, the issue is that StatusField.contribute_to_class check for cls.STATUS, but because and logic on Django you can't assert that STATUS exists on new_class because attrs.items() is a dict, not SortedDict or list. You don't have order and should not check for other attributes.

Fortunately we have 'no_check_for_status' argument that you use for South, but aniway we are handling that stuff wrong because 'contribute_to_class' can't expect on this django loop at base.py#L98. May if we change it to use 'class_prepared' signal we do right thing?

Can't use StatusField with Django 1.7 and new App registry

Trying to use a StatusModel or StatusField in Django 1.7 will try to connect to a class_prepared Signal which isn't quite ready at the time.

Traceback:

File "/Users/jwa/Projects/demo/apps/plans/models.py", line 413, in <module>
  class Subscription(StatusModel, TimeStampedModel):
File "/Users/jwa/.virtualenvs/demo/lib/python3.3/site-packages/django/db/models/base.py", line 297, in __new__
  new_class._prepare()
File "/Users/jwa/.virtualenvs/demo/lib/python3.3/site-packages/django/db/models/base.py", line 355, in _prepare
  signals.class_prepared.send(sender=cls)
File "/Users/jwa/.virtualenvs/demo/lib/python3.3/site-packages/django/dispatch/dispatcher.py", line 198, in send
  response = receiver(signal=self, sender=sender, **named)
File "/Users/jwa/.virtualenvs/demo/lib/python3.3/site-packages/model_utils/models.py", line 63, in add_status_query_managers
  sender._meta.get_field(name)
File "/Users/jwa/.virtualenvs/demo/lib/python3.3/site-packages/django/db/models/options.py", line 389, in get_field
  if f.name == name:
File "/Users/jwa/.virtualenvs/demo/lib/python3.3/site-packages/django/utils/functional.py", line 164, in __eq__
  return self.__cast() == other
File "/Users/jwa/.virtualenvs/demo/lib/python3.3/site-packages/django/utils/functional.py", line 152, in __cast
  return self.__text_cast()
File "/Users/jwa/.virtualenvs/demo/lib/python3.3/site-packages/django/utils/functional.py", line 143, in __text_cast
  return func(*self.__args, **self.__kw)
File "/Users/jwa/.virtualenvs/demo/lib/python3.3/site-packages/django/utils/translation/__init__.py", line 80, in ugettext
  return _trans.ugettext(message)
File "/Users/jwa/.virtualenvs/demo/lib/python3.3/site-packages/django/utils/translation/trans_real.py", line 310, in gettext
  return do_translate(message, 'gettext')
File "/Users/jwa/.virtualenvs/demo/lib/python3.3/site-packages/django/utils/translation/trans_real.py", line 297, in do_translate
  _default = translation(settings.LANGUAGE_CODE)
File "/Users/jwa/.virtualenvs/demo/lib/python3.3/site-packages/django/utils/translation/trans_real.py", line 199, in translation
  default_translation = _fetch(settings.LANGUAGE_CODE)
File "/Users/jwa/.virtualenvs/demo/lib/python3.3/site-packages/django/utils/translation/trans_real.py", line 182, in _fetch
  for app_config in reversed(list(apps.get_app_configs())):
File "/Users/jwa/.virtualenvs/demo/lib/python3.3/site-packages/django/apps/registry.py", line 124, in get_app_configs
  self.check_ready()
File "/Users/jwa/.virtualenvs/demo/lib/python3.3/site-packages/django/apps/registry.py", line 118, in check_ready
  raise RuntimeError("App registry isn't ready yet.")

RuntimeError: App registry isn't ready yet.

Pickle fails with models using FieldTracker

PicklingError: Can't pickle <function save at 0x3e10320>: it's not found as model_utils.tracker.save

This happens with custom User model in Django 1.5 (not sure if it matters). Haven't had a chance to try it with earlier versions or 1.6.

bulk deletion of subclass objects not working

If I create a series of models roughly like this:

class A(PolymorphicModel):
some fields ...

class B(A):
# no extra fields... just new methods

class C(A):
# no extra fields... just new methods
....

then create some objects:

B().save()
C().save()

and then try and delete them:

A.objects.all().delete()

then I get an error something like "'C' object has no attribute 'b_ptr'"

Whereas if I delete them individually:

[i.delete() for i in A.objects.all()] 
>[None, None]

then all is fine. I'm not sure if this is a bug or a feature, but it's frustrating because I'm going to have to override the admin to make deletion of inline objects work (I'm using B and C only to override methods on A, no additional fields are stored).

Use Travis CI for continuous integration tests

Have you considered using Travis CI for testing?

It's easy to run the tests locally, but Travis could be useful for automatically running the tests against multiple versions of Django.

Feature: undo() functionality for FieldTracker()

I was reading the docs about this awesome library and noticed this FieldTracker() utility could lead to better website UX if it had a built-in undo() which would revert a model instance to the previous version of the model (maybe because the user clicked on "Save" when he didn't mean to)

(I am referring to the good UX practices described in these two articles
http://www.philweber.com/articles/why_message_boxes_are_evil.htm
http://alistapart.com/article/neveruseawarning)

QueryManager constraints don't apply to RelatedManagers

What?

Let's say you have a model like this:

class Article(models.Model):
    author = models.ForeignKey('Author')
    published = models.BooleanField()

    published_only = QueryManager(published=True)

Note that published_only is the default manager here.

Now if you do

an_author.article_set...

the published=True constraint is NOT added to the query.

Why?

When you access a related model that way, Django internally subclasses the model's default manager (db/models/fields/related.py, ForeignRelatedObjectsDescriptor:related_manager_cls) and uses that manager for the query. The issue here is that by the current implementation, django-model-utils' QueryManager keeps the constraints as instance state and not as class state. Which obviously means that the constraints are lost in the Django-generated related manager.

Solution 1

Fix this in Django: The Django-generated manager class should not only extend the default manager's class but the concrete instance. By not using inheritance for example.

Solution 2

Make QueryManager(...) call actually generate a subclass that incorporates the constraints (arguments). Probably easier but doesn't feel "right".

No select_related possible with InheritanceManager

Since the InheritanceQuerySet uses select_related to join children objects, all previous select_related calls will be discarded. Exemple:

class Product(models.Model):
    objects = InheritanceManager()
    client = models.ForeignKey('clients.Client')
    price = models.PositiveIntegerField()


class Shirt(Product):
    color = …

class Pants(Product):
    …

products = Product.objects.all().select_related('client').select_subclasses() #client table will not be joined

Document alternative usages of PassThroughManager

Example:

class CustomQuerySet(QuerySet):
     pass

class CustomManager(PassThroughManager):
    def __init__(self):
         return super(CustomManager, self).__init__(queryset_cls=CustomQuerySet)

This issue should be resolved at the same time as #56.

CharFields become null = True when using Choices

Hi Carl,

I'm using your Choices class in my project. I have realized that when I use it with a CharField, if I create an instance without specifying an option, the empty string is used and no exception is raised. This is the code:

choicesReach = Choices(
        ('onPickup', _('A la recogida')),
        ('onDelivery', _('A la entrega')),
)
reach = models.CharField(max_length=10, choices=choicesReach)

Even if I specify:

models.CharField(choices = choicesReach, max_length = 10, blank = False, null = False)

It still lets me create objects without specifying a reach field. The reach column is NOT NULL in the DB.

is this the expected behavior? am I doing anything wrong?

Thanks, regards
Miguel Araujo

Introduce functionality on InheritanceManager to select_subclasses via model classes

When 1.6 lands, fixing the various issues with select_related() and concrete model inheritance, there is an opportunity for exposing a cleaner API that requires less developer forethought, at the potential cost of behind-the-scenes magic in model-utils.

Current functionality

Given a collection of model classes:

class A(models.Model): pass
class B(A): pass
class C(B): pass
class D(A): pass
# and so on

the naive, but easiest approach to downcasting is just to blindly run select_subclasses() without tethering which relations ought to be fetched. This is one LEFT OUTER JOIN per child, which isn't problematic as long as the number of classes deriving from the bases is small.

Often-times, a query may only want certain relations, so might do select_subclasses('b') or whatever. This is currently easy to keep track of in one's head, by virtue of not being able to go more than 1 descendent deep, because of the aforementioned ORM limitations.

In the future, to tether to only certain relations, one would do something like select_subclasses('b', 'b__c', 'b__c__d') or whatever, denoting the extra depth of relations available; but keeping track of this becomes more burdensome as more depth or variants are introduced (Is D a subclass of C, or A?)

Proposed syntax sugar

Note: for clarity, I'm referring to a separate method, but in brief discussions Carl was in favour of attempting to amalgamate it into select_subclasses

A function such as select_subclasses_for_models would have the same method signature as select_subclasses, but instead of consuming a list of string relations, would accept a list of classes which should be introspected to ascertain the relations to add to the underlying select_related query.

Internally, it would have to validate that somewhere in the _meta.parents it has a relation to the class it's being called from (self.model on the manager?):

A.objects.all().select_subclasses_for_models(C, D)
# C and D must be subclasses of A
# internally probably ends up selecting, 'b__c', 'd'
# the queryset will only select, and yield, instances of either A, C, or D.

To add to the complexity, any class which isn't a direct descendent (A->B) would need to backtrack up it's parents and establish the related name all the way back to A, or whatever parent class it's being called from.

Qualms

Arguably, one should know the data structure well enough ahead of time to be able to get by without this, especially if one is already in the realms of wanting to avoid bare select_subclasses for performance reasons et al. It would add significantly to the upkeep-cost of InheritanceManager, whether as a new method or an addendum to the existing API method.

It also raises the possibility of circular import problems, as it would need direct access to the class to select, which means importing it where it might otherwise not be. This problem doesn't exist with the current implementation, because there's nothing to import and pass on.

It also introduces a query syntax which is entirely alien to the Django methods currently being mirrored (ie: passing a list of relations by related name), which may not be desirable.

Add mutable fields support

Suppose we have a custom mutable-field (or similar, for example json)

from django.db import models

class MutableField(models.TextField):

    __metaclass__ = models.SubfieldBase

    def to_python(self, value):
        if value == '':
            return None

        try:
            if isinstance(value, basestring):
                return [int(i) for i in value.split(',')]
        except ValueError:
            pass

        return value

    def get_db_prep_save(self, value, connection):
        if not value:
            return ''

        if isinstance(value, list):
            value = ','.join((str(i) for i in value))

        return super(MutableField, self).get_db_prep_save(value, connection)

and a simple model

class Post(models.Model):
    title = models.CharField(max_length=100)
    text = models.CharField(max_length=150)

    mutable = MutableField()

Now if I have a Post instance and I change the field mutable, then I can not see if something has changed in it, because saved_data refers to the original dictionary, and not a copy, so changes of mutable fields also result to changes of the stored saved data (dict is mutable, captain obviuos).

I corrected this behavior thus adding support for mutable-fields.

For more information see my pull request: #73

Error installing from pypi on python 3.3.3

I'm trying to install django-model-utils from pypi by

pip install django-model-utils

but the following error is raised:

Downloading/unpacking django-model-utils
  Downloading django-model-utils-2.0.1.tar.gz
  Running setup.py (path:/Users/simo/.virtualenvs/mezzanine3_py33/build/django-model-utils/setup.py) egg_info for package django-model-utils
    Traceback (most recent call last):
      File "<string>", line 17, in <module>
      File "/Users/simo/.virtualenvs/mezzanine3_py33/build/django-model-utils/setup.py", line 6, in <module>
        open('CHANGES.rst').read() +
      File "/Users/simo/.virtualenvs/mezzanine3_py33/bin/../lib/python3.3/encodings/ascii.py", line 26, in decode
        return codecs.ascii_decode(input, self.errors)[0]
    UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 5020: ordinal not in range(128)
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):

  File "<string>", line 17, in <module>

  File "/Users/simo/.virtualenvs/mezzanine3_py33/build/django-model-utils/setup.py", line 6, in <module>

    open('CHANGES.rst').read() +

  File "/Users/simo/.virtualenvs/mezzanine3_py33/bin/../lib/python3.3/encodings/ascii.py", line 26, in decode

    return codecs.ascii_decode(input, self.errors)[0]

UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 5020: ordinal not in range(128)

Regards,
Simone

Use "Python identifier" from three-tuple Choices in StatusModel

Hello Carl.

I started to use StatusModel this week (amazing tool btw) and with model like that:

class MyModel(StatusModel):
    STATUS = Choices(
        ('0', 'nao_atendido', 'Não atendido'),
        ('10', 'em_atendimento', 'Em atendimento'),
        ('11', 'contato_sem_sucesso', 'Contato sem sucesso'),
        ('20', 'atendido_sem_sucesso', 'Atendido sem sucesso'),
        ('21', 'atendido_com_sucesso', 'Atendido com sucesso')
    )

If I do:

>>> x = MyModel(status=MyModel.STATUS.nao_atendido)
>>> x.status
u'0'
>>> x.0
x.0
  ^
SyntaxError: invalid syntax

Same happens with a saved instance.

So, IMO, in case of tree tuple choice, it should use second item instead first item to create managers, and, never use a digit.

I love to write patches for you, but since I'm not needing this feature right now and without time I just created that issue.

get_query_set isn't called unless you call .all()

Here are my QuerySet, PassThroughManager, and model classes:

class PremiumSubmissionCommentQuerySet(QuerySet):
    def by_mentors(self):
        return self.filter(user__profile__is_mentor=True)

    def by_students(self):
        return self.exclude(user__profile__is_mentor=True)\
            .exclude(user__is_superuser=True)

    def in_course(self, course):
        return self.filter(submission__project__unit__course=course)

class PremiumSubmissionCommentManager(PassThroughManager):
    def __init__(self):
        return super(PremiumSubmissionCommentManager, self)\
            .__init__(queryset_cls=PremiumSubmissionCommentQuerySet)

    def get_query_set(self):
        return super(PremiumSubmissionCommentManager, self).get_query_set()\
            .exclude(submission__project=None)

class PremiumSubmissionComment(SubmissionComment):
    objects = PremiumSubmissionCommentManager()

    class Meta:
        verbose_name = 'Premium submission comment'
        proxy = True

The problem is that my get_query_set method is called when I do PremiumSubmissionComment.objects.all().by_mentors() but it's NOT called when I do PremiumSubmissionComment.objects.by_mentors().

Is that the expected behavior for some reason? If so, how can I achieve my goal of changing the "default" QuerySet, just like you can do by defining a vanilla custom Manager where it doesn't care if you call .all() or not?

ModelTracker has_changed bug after first save

Here's a model using a model tracker:

from django.db import models
from model_utils import ModelTracker

class Person(models.Model):
name = models.CharField(max_length=100)
tracker = ModelTracker(fields=['name'])

I want to connect to the post_save hook for a model and check whether the name field has been changed.

The problem: after saving the model for the first time self.tracker.has_changed('name') will raise a FieldError in the post-save hook because the original value of the field wasn't defined.

Per the first sentence in this comment and the next one, I think this is a bug. Calling has_changed on a tracked field that has no history yet should return None.

Add a Single Table Inheiretance polymorphic model manager

Would you consider adding a polymorphic model manager similar to this one? It works a lot like the InheritanceManager that is already in this project except it works by having a discriminator column and proxy models for each of the subclasses. With this addition, django-model-utils would be able to support all 3 of the common model inheritance techniques:

  • Concrete-table inheritance
    (each subclass has a table with all of the columns, no parent table)
  • Class-table Inheritance
    (each subclass has a table only for what is unique, parent model also has a table)
  • Single-table inheritance
    (everything is stored in a single table and a designated discriminator column is used to determine the type)

The third option (STI) gives you the benefits of the second (CTI) but without the join for the children. You can reference both the parent and the child objects as a foreign key without resorting to a GenericForeignKey and still define child-specific python behavior in the child proxy objects. The tradeoff is that you cannot make columns that are in some children but not in others nullable. This is a good tradeoff when the primary difference is in python behavior more than data.

I think some small changes to the technique would be needed to match the style of the existing InheritanceManager api but they expose similar behavior.

Allow selecting of subclass level

I'd like to suggest adding 'level' argument to searching subclasses via methods 'select_subclasses' and 'get_subclass'.

This would allow for searching for a "middle" -class in multilevel inheritance situations.
For example I have structure "Sensor" -> "TemperatureSensor" -> "OnewireTemperaturesensor". Currently "get_subclass" returns "OnewireTemperaturesensor" always, when in some cases it'd be useful to return "TemperatureSensor" to identify the sensor type more easily.

Custom model Manager get_query_set extra not working

Good day,

I can't seem to get the extra select results when using an Inheritance manager and I can't seem to find any documentation on how to sub class InheritanceManager and override the get_query_set method.

Example:

class GeneralManager(InheritanceManager):
    def get_query_set(self):
        return super(GeneralManager, self).get_query_set().extra(select={'counter': 'some sql'})

class General(models.Model):
    name = models.CharField(max_length = 30)
    objects = GeneralManager()

class Car(General):
    pass

class Truck(General):
   pass

When using this kind of model structure when I do a General.objects.select_subclasses() each object in the queryset no longer has the 'counter' sub select. If I remove the select_subclasses they all have the 'counter' sub select.

Any help would be highly appreciated.

PassThroughManager methods are not available for tab-completion/discovery

Custom manager methods for PassThroughManager don't show up for iPython tab completion. They also aren't included in the output of the dir() function.

This is mostly a usability issue. I suppose it also extends to problems with introspection -- if you are using existing code that inspects the contents returned by dir() instead of hasattr()

Example:

The PassThroughManager for this model defines the manager method a_test_manager_method().

In[10]: MyModel.objects.a_test_manager_method()
Out[10]: [<MyModel: 33bb1701-2148-4d1b-9479-ac330d04a852>]

In[11]: dir(MyModel.objects)
Out[11]: 
['__class__',
 '__delattr__',
 '__dict__',
 '__doc__',
 '__format__',
 '__getattr__',
 '__getattribute__',
 '__hash__',
 '__init__',
 '__module__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 '_copy_to_model',
 '_db',
 '_deny_methods',
 '_inherited',
 '_insert',
 '_queryset_cls',
 '_set_creation_counter',
 '_update',
 'aggregate',
 'all',
 'annotate',
 'bulk_create',
 'complex_filter',
 'contribute_to_class',
 'count',
 'create',
 'creation_counter',
 'dates',
 'db',
 'db_manager',
 'defer',
 'distinct',
 'exclude',
 'exists',
 'extra',
 'filter',
 'for_queryset_class',
 'get',
 'get_empty_query_set',
 'get_or_create',
 'get_query_set',
 'in_bulk',
 'iterator',
 'latest',
 'model',
 'none',
 'only',
 'order_by',
 'prefetch_related',
 'raw',
 'reverse',
 'select_for_update',
 'select_related',
 'update',
 'using',
 'values',
 'values_list']

In[12]: "a_test_manager_method" in dir(UpstreamObject.objects)
Out[12]: False

Nesting Choices

I'm in the process of converting a bunch of choice tuples to Choices. Currently I nest them to make them easier to read in the admin:

BOOK_TYPES = (
    ('"Photo" Books', (
        (PHOTOBOOK, 'Photobook'),
        (LIVE_PHOTOBOOK, 'Live Photobook'),
        (PHOTO_COLLECTION, 'Photo Collection')
    )),
    ('Other', (
        (ESSAY, 'Essay Book'),
        (OFFSHOT, 'Off-Shot'),
        (OTHER, 'Other')
    ))
)

Is there a way to do this with Choices?

MonitorField with 'when' inits to now?

In my use-case, I have a StatusField with three options and two MonitorFields that use 'when' to watch for a specific status. See abbreviated model below.

When I initialize a new object, it appears that both active_date and deprecated_date are set to the current time. Is this the intended behavior? It would make more sense to me if these MonitorFields were left Null...

class Element(models.Model):
    STATUS = Choices('draft', 'active', 'deprecated')
    status = StatusField()
    created_date = models.DateTimeField('Element creation date', auto_now_add=True, null=False, editable=True)
    active_date = MonitorField('Element active date', monitor='status', when=['active'], null=True)
    deprecated_date = MonitorField('Element deprecation date', monitor='status', when=['deprecated'], null=True)

Inheritance for foreign keys

Hi,

I cannot find a way to use the wonderful (it saves my life) InheritanceManager through a ForeignKey. Here's some code, it will be clearer:

class Product(Model):
    objects = InheritanceManager()

class Shirt(Product):
    …

class Jacket(Product):
    …

class Box(Model):
    product = ForeignKey('Product')

boxes = Box.objects.all()
for box in boxes:
    box.product # How to get shirts and jackets here?

Is there any way to do this? Cannot find it in the doc. If not, that would be a great feature to add.

Regards,
Thibault

tracker.previous on FileField does not work

Django and model_utils versions are:

django.VERSION
(1, 5, 5, 'final', 0)
model_utils.version
'1.5.0'

Repro steps:
Create model.

class NewModel(models.Model):
    my_file = models.FileField(upload_to='.')
    tracker = FieldTracker(fields=['my_file',])

In admin upload some file, save. Try to get previous file field.

f = NewModel.objects.get(id=1)

prev_file = f.tracker.previous('my_file')

prev_file.path

Traceback (most recent call last):
File "", line 1, in
File "/Users/andriyko/.virtualenvs/rfdocset/lib/python2.7/site-packages/django/db/models/fields/files.py", line 59, in _get_path
return self.storage.path(self.name)
AttributeError: 'FieldFile' object has no attribute 'storage'

Workaround (assumes I have some default_storage):

#did not want to override .previous() method and do field type checking
class CustomFieldInstanceTracker(FieldInstanceTracker):
    def previous_file(self, field, storage=None):
        new_field = self.previous(field)
        setattr(new_field, 'storage', storage or default_storage)
        return new_field

Then in models.py patch FieldTracker:

FieldTracker.tracker_class = CustomFieldInstanceTracker

Use as:

prev_file = f.tracker.previous_file('my_file')

prev_file.path

u'/Users/andriyko/Projects/pr_test/test_files/test.txt'

FieldTracker breaks with inheritance

Hello,

Assume I have a model similar to this:

class Transaction(TimeStampedModel):
    status = models.CharField(max_length=1, choices=TransactionStatusChoices.choices, null=False, blank=False, default=TransactionStatusChoices.Pending)

    objects = InheritanceManager()
    status_tracker = FieldTracker(fields=['status'])

    def save(self, *args, **kwargs):
        ret = super(Transaction, self).save(*args, **kwargs)

        # Check if status has changed and if it did send a signal
        if self.status_tracker.has_changed():
            print 'SEND'

        return ret

class BitcoinPaymentTransaction(Transaction):
    coinbase_id = models.CharField(null=False, blank=False, max_length=250)

I get a list of transactions and modify one of them:

x = list(p.transaction_set.select_subclasses().all())
print x
x[0].status = TransactionStatusChoices.Fail
x[0].save()

I will then get this:

 File "/home/eran/fc/testsite/payments/management/commands/payments_test.py", line 65, in handle
    x[0].save()
  File "/home/eran/fc/testsite/payments/models.py", line 76, in save
    if self.status_tracker.has_changed():
  File "/home/eran/fc/env/local/lib/python2.7/site-packages/model_utils/tracker.py", line 95, in __get__
    return getattr(instance, self.attname)
AttributeError: 'BitcoinPaymentTransaction' object has no attribute '_status_tracker'

If I try this without select_subclasses it seems to work fine

Choices documentation error

With :
STATUS = Choices((0, 'draft', _('draft')), (1, 'published', _('published')))
model.get_status_display() will return an integer

But using :

STATUS = Choices(('0', 'draft', _('draft')), ('1', 'published', _('published')))
model.get_status_display() will return the correct value

Lying about the modified date for a Timestamped model is difficult.

A bit of prelude:
Sometimes it is useful to be able to lie to the database about when things happened. Mostly this kind of thing is useful when copying/cloning existing objects (or their attributes to another instance).
Django doesn't make this easy with it's auto_now/auto_now_add attributes for date based fields, as they always take precedence:

class MyModel(Model):
    created = DateTimeField(auto_now_add=True)
    modified = DateTimeField(auto_now=True)

It is impossible to lie to the database efficiently:

old = datetime.now() - timedelta(days=365)
obj, created = MyModel.objects.create(created=old, modified=old)
# the inserted object won't have the value held in `old`
obj.created = old
obj.save() 
# now it will.

So that rules Django's builtin timestamping out.
In model utils, we have TimestampedModel which itself is just a collection of the two Auto fields; using this model we can get slightly further in our lying:

class MyModel2(TimestampedModel): pass

now we can lie efficiently about our creation time:

old = datetime.now() - timedelta(days=365)
obj, created = MyModel2.objects.create(created=old, modified=old)
# the inserted object will have the `old` value for created, but not modified.

but not our modified time, which doesn't test the model instance for the attr's existance before applying it's own.

Personally, I'd be keen to have the lying be possible, but it would be technically backwards incompatible to do so: users might be supplying a modified date and relying on it not being applied (perhaps by having a blanket set of **kwargs and not popping modified out)

As there are no tests I can see demonstrating a preferred intention, I'm opening this for discussion on whether there's merit to perhaps allowing this.

Access the parent model

Hello,

I use InheritanceManager module and I'm trying to get the parent model on select_subclasses() queries. How to get only the parent model on a query like this?

posts_list = PostType.objects.order_by('-updated_at').select_subclasses()

for post in posts_list:
    print post.parent

Thanks.

KeyError on save with update_fields in which there is field not tracked in model

Suppose we have a simple model:

class Post(models.Model):
    title = models.CharField(max_length=123)
    text = models.CharFields(max_length=777)

    tracker = FieldTracker(fields=['text'])

Let's create one instance in the database:

Post.objects.create(title='Post Title', text='Post Text')

Now we get an object from the database and change untracked field then save it:

post = Post.objects.get()
post.title = 'New Post Title'
post.save(update_fields=['title'])

Booom!

Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "/Users/m.silonov/Projects/django-model-utils/model_utils/tracker.py", line 101, in save
    fields=kwargs.get('update_fields')
  File "/Users/m.silonov/Projects/django-model-utils/model_utils/tracker.py", line 21, in set_saved_fields
    self.saved_data.update(**self.current(fields=fields))
  File "/Users/m.silonov/Projects/django-model-utils/model_utils/tracker.py", line 27, in current
    return dict((f, self.get_field_value(f)) for f in fields)
  File "/Users/m.silonov/Projects/django-model-utils/model_utils/tracker.py", line 27, in <genexpr>
    return dict((f, self.get_field_value(f)) for f in fields)
  File "/Users/m.silonov/Projects/django-model-utils/model_utils/tracker.py", line 13, in get_field_value
    return getattr(self.instance, self.field_map[field])
KeyError: 'title'

I introduced my solution and made pull request.
For more information: #70

select_subclasses not work with multi model inheritance

select_subclasses can't cast to model that was inherited from few models.

Code example:

class E(models.Model):
    objects = InheritanceManager()

    name = models.CharField(max_length=255)

class A(models.Model):
    namea = models.CharField(max_length=255)

class B(E, A):
    nameb = models.CharField(max_length=255)
B.objects.create(name='be', namea='ba', nameb='bb')
print E.objects.all().select_subclasses()
>>> []

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.