#17080: Custom fields using attname, Model __init__ and Deserialization: 
hard-coded
magic for relation fields
-------------------------------+--------------------
     Reporter:  anentropic     |      Owner:  nobody
         Type:  Bug            |     Status:  new
    Component:  Uncategorized  |    Version:  1.3
     Severity:  Normal         |   Keywords:
 Triage Stage:  Unreviewed     |  Has patch:  0
Easy pickings:  0              |      UI/UX:  0
-------------------------------+--------------------
 I'm struggling with the title for this issue, sorry.

 Basically: if you have custom field type where attname is different to
 name (which can be desirable for same reason it's done in ForeignKey
 fields) then parts of Django don't work and are not practical to override.

 Your field will work okay until you try to do this:

 {{{
     obj = MyModel(my_custom_field_name=val)
 }}}

 At this point Model.!__init!__ in django/db/models/base.py complains with:

 {{{
     TypeError: 'my_custom_field_name' is an invalid keyword argument for
 this function
 }}}

 Looking in !__init!__ it is clear why:

 You've got a check for '''isinstance(field.rel, ManyToOneRel)''' to decide
 if '''is_related_object = True''' and then:

 {{{
     if is_related_object:
         setattr(self, field.name, rel_obj)
     else:
         setattr(self, field.attname, val)
 }}}

 ie it decides which value to populate on the model. After that we loop
 through kwargs and check:

 {{{
     if isinstance(getattr(self.__class__, prop), property):
         setattr(self, prop, kwargs.pop(prop))
 }}}

 Which fails because '''is_related_object=False''' for our custom field so
 '''attname''' has been set on the model but '''name''' was used in kwargs.
 After that the TypeError is raised.

 Ok you say, you should just always instantiate your model using the
 attname as a kwarg instead of name.

 Unfortunately the Django serialization apparatus has more hard-coded magic
 that makes ForeignKey and ManyToMany fields work, but ensures your custom
 field will fail in this regard.

 In django/core/serializers/python.py Deserializer class we have checks for
 '''if field.rel''' with special cases for ManyToManyRel and ManyToOneRel,
 falling through to...

 {{{
         else:
             data[field.name] = field.to_python(field_value)

     yield base.DeserializedObject(Model(**data), m2m_data)
 }}}

 ...instantiating the model with '''field.name''' as the default case.

 Ok, so I tried adding a dummy rel=ManyToOneRel attribute on my custom
 field to trick Model.!__init!__ ... but then that triggers more magic in
 the Serializer class, which understandably tries to treat it as a
 ForeignKey field.

 Right now I can't see any way to cleanly fake my way out.

 Adding this line to django/core/serializers/python.py Deserializer fixed
 it for me:

 {{{
             elif field.name != field.attname:
                 data[field.attname] = field.to_python(field_value)

             # Handle all other fields
             else:
                 data[field.name] = field.to_python(field_value)
 }}}

-- 
Ticket URL: <https://code.djangoproject.com/ticket/17080>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To post to this group, send email to django-updates@googlegroups.com.
To unsubscribe from this group, send email to 
django-updates+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-updates?hl=en.

Reply via email to