Wednesday, November 26, 2014

Re: metaclass on model class calling db.models.options method vs django 1.7

Hi,

You could try using [f.name for f in _meta.fields] instead of _meta.get_all_field_names(). It won't include ManyToMany, but it's more likely to be available at this point.

Collin


On Wednesday, November 26, 2014 11:45:22 AM UTC-5, Matthieu Rigal wrote:
Hi Collin,

Thanks for the hint on get_field(), I had read it, but now I will make sure to update it soon :-)

Here is the code of one metaclass:

@classmethod
def post_new(cls, model, attrs):
set_item_field_names = model._meta.get_all_field_names()
if not 'item' in set_item_field_names:
return
item_model = model._meta.get_field_by_name('item')[0].rel.to

# model and field are passed through from the add_lazy_relation call.
# target is guaranteed to be a class, even if passed to add_lazy_relation as a string
def add_passthrough_fields(field, target, model):
item_field_names = target._meta.get_all_field_names()
for item_field_name in item_field_names:
if not item_field_name == 'id' and item_field_name not in dir(model):
if item_field_name in set_item_field_names:
raise KeyError('\'{}\' must not be overwritten'.format(item_field_name))
else:
setattr(model, item_field_name, PassThrough(item_field_name))
setattr(model, model.TYPE_FIELD_NAME, PassThrough(model.TYPE_FIELD_NAME, read_only=True))

# args = model (the source), field (if applicable), target (foreign key reference - string or class)
add_lazy_relation(model, None, item_model, add_passthrough_fields)

And here is a backtrace. I can even trigger it by running ./manage.py help in my virtual env


Traceback (most recent call last):
  File "django_project/manage.py", line 16, in <module>
    execute_from_command_line(sys.argv)
  File "/Users/mrigal/dev/_virtualenvs/dj-1.7/lib/python2.7/site-packages/django/core/management/__init__.py", line 385, in execute_from_command_line
    utility.execute()
  File "/Users/mrigal/dev/_virtualenvs/dj-1.7/lib/python2.7/site-packages/django/core/management/__init__.py", line 354, in execute
    django.setup()
  File "/Users/mrigal/dev/_virtualenvs/dj-1.7/lib/python2.7/site-packages/django/__init__.py", line 21, in setup
    apps.populate(settings.INSTALLED_APPS)
  File "/Users/mrigal/dev/_virtualenvs/dj-1.7/lib/python2.7/site-packages/django/apps/registry.py", line 108, in populate
    app_config.import_models(all_models)
  File "/Users/mrigal/dev/_virtualenvs/dj-1.7/lib/python2.7/site-packages/django/apps/config.py", line 202, in import_models
    self.models_module = import_module(models_module_name)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/importlib/__init__.py", line 37, in import_module
    __import__(name)
  File "/Users/mrigal/dev/my.proj/django_project/proj/md/models/__init__.py", line 5, in <module>
    from proj.md.models.printers import (
  File "/Users/mrigal/dev/my.proj/django_project/proj/md/models/printers.py", line 6, in <module>
    from proj.md.models.base import (
  File "/Users/mrigal/dev/my.proj/django_project/proj/md/models/base.py", line 203, in <module>
    class AbstractmdSetItem(mdUpdateModelMixin, models.Model):
  File "/Users/mrigal/dev/my.proj/django_project/proj/md/models/meta.py", line 33, in __new__
    cls.post_new(model, attrs)
  File "/Users/mrigal/dev/my.proj/django_project/proj/md/models/meta.py", line 83, in post_new
    set_item_field_names = model._meta.get_all_field_names()
  File "/Users/mrigal/dev/_virtualenvs/dj-1.7/lib/python2.7/site-packages/django/db/models/options.py", line 432, in get_all_field_names
    cache = self.init_name_map()
  File "/Users/mrigal/dev/_virtualenvs/dj-1.7/lib/python2.7/site-packages/django/db/models/options.py", line 445, in init_name_map
    for f, model in self.get_all_related_m2m_objects_with_model():
  File "/Users/mrigal/dev/_virtualenvs/dj-1.7/lib/python2.7/site-packages/django/db/models/options.py", line 563, in get_all_related_m2m_objects_with_model
    cache = self._fill_related_many_to_many_cache()
  File "/Users/mrigal/dev/_virtualenvs/dj-1.7/lib/python2.7/site-packages/django/db/models/options.py", line 577, in _fill_related_many_to_many_cache
    for klass in self.apps.get_models():
  File "/Users/mrigal/dev/_virtualenvs/dj-1.7/lib/python2.7/site-packages/django/utils/lru_cache.py", line 101, in wrapper
    result = user_function(*args, **kwds)
  File "/Users/mrigal/dev/_virtualenvs/dj-1.7/lib/python2.7/site-packages/django/apps/registry.py", line 168, in get_models
    self.check_models_ready()
  File "/Users/mrigal/dev/_virtualenvs/dj-1.7/lib/python2.7/site-packages/django/apps/registry.py", line 131, in check_models_ready
    raise AppRegistryNotReady("Models aren't loaded yet.")
django.core.exceptions.AppRegistryNotReady: Models aren't loaded yet.

I also wouldn't know what to add to app.ready(), since the models themselves import their abstract base classes which include themselves the metaclasses.

Best,
Matthieu

On Wednesday, November 26, 2014 2:57:28 PM UTC+1, Collin Anderson wrote:
Hi,

I feel like there's almost always a way to avoid metaclasses.

Have you tried adding your relations in the
 
signal?

For more context, could you post some of your metaclass code and maybe part of a "AppRegsitryNotReady" traceback or two?

Also, get_field_by_name(name)[0] will be deprecated soon. Use .get_field(name) instead.

Collin


On Tuesday, November 25, 2014 7:05:54 AM UTC-5, Matthieu Rigal wrote:
Hi guys,

I am using a metaclass to standardize how I link two classes, on Django 1.6. This is done in order to have a generic way of connecting classes without having to know the name of the fields being linked.

Basically, I have in my metaclass 'post_new' method to get the 'other' model, using model._meta.get_field_by_name('item') I use this afterward like following add_lazy_relation(model, None, item_model, add_passthrough_fields) 

I add these PassThrough fields again getting the fields from the meta, overwriting the getter and the setter.

Upgrading to Django 1.7 is impossible with what I'm currently doing. I get tons of AppRegistryNotReady , since it is going through this post_new method at the very beginning. 

Maybe you have a suggestion how I could get around this or which other methods to call to call later (but not too late, those are metaclasses) in order to get the list of fields. Maybe some code snippets running on Django 1.7 and using models with metaclasses could already help.

Thanks in advance,
Matthieu

PS: I know it is a bit uncommon to do so and if you don't need to do so and use these metaclasses, you shouldn't ;-)

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django-users@googlegroups.com.
Visit this group at http://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/9794d04c-02eb-4703-95e7-7e801b4f173b%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

No comments:

Post a Comment