Django: models.Field.from_db_value() wird nicht aufgerufen..

Django, Flask, Bottle, WSGI, CGI…
Antworten
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Hab ein models.Field wie bei https://docs.djangoproject.com/en/dev/h ... on-objects beschrieben:

Code: Alles auswählen

class DictModelField(models.Field):
    description = "DictModelField"
    __metaclass__ = models.SubfieldBase
...
    def to_python(self, value):
        return self.from_db_value(value)

    def from_db_value(self, value, expression=None, connection=None, context=None):
        ...
        return DictData(value)
Das funktioniert auch bei "normalen" QuerySets also sowas wie:

Code: Alles auswählen

instance = MeinModel.objects.all()[0]
print(instance.mein_DictModelField)
Allerdings wird weder das alte to_python() noch das neuere from_db_value() aufgerufen, wenn .values() genutzt wird.
bsp.:

Code: Alles auswählen

MeinModel.objects.all().values("mein_DictModelField")
Ist das ein Bug, oder mache ich was falsch?!?

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Ach, wobei from_db_value(), bei normalen queryset, auch nur mit Python 2 aufgerufen wird, bei Python 3 aber nicht?!?

EDIT: gerade https://docs.djangoproject.com/en/1.8/r ... bfieldbase gefunden:
SubfieldBase

django.db.models.fields.subclassing.SubfieldBase has been deprecated and will be removed in Django 2.0. Historically, it was used to handle fields where type conversion was needed when loading from the database, but it was not used in .values() calls or in aggregates. It has been replaced with from_db_value(). Note that the new approach does not call the to_python() method on assignment as was the case with SubfieldBase.
Was heißt das jetzt ?
Das alte to_python() wird demnach nicht bei .values() aufgerufen.
Aber from_db_value() sollte doch in jedem Fall aufgerufen werden, oder?!?

EDIT: Ach, das ist ja dann erst für 1.8 der Fall... Egal. ich suche eine Lösung für 1.7 und 1.8...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

So, hab mal ein Beispiel gemacht:

EDIT: So jetzt bin ich verwirrt...

Ich hab mal die Unittests gegen "stable/1.7.x", "stable/1.8.x" und "master" gemacht.
https://github.com/jedie/django/branches/yours

Änderungen im Überblick:
gegen "stable/1.7.x": https://github.com/jedie/django/compare ... elds_1.7.x
gegen "stable/1.8.x": https://github.com/jedie/django/compare ... elds_1.8.x
gegen "master": https://github.com/jedie/django/compare ... lds_master

Ergebnis:

1.7.x mit Py2:

Code: Alles auswählen

test_custom_model_field (custom_model_fields.tests.TestModel1Tests) ... FAIL
test_values (custom_model_fields.tests.TestModel1Tests) ... FAIL
test_custom_model_field (custom_model_fields.tests.TestModel2Tests) ... ok
test_values (custom_model_fields.tests.TestModel2Tests) ... FAIL
1.7.x mit Py3:

Code: Alles auswählen

test_custom_model_field (custom_model_fields.tests.TestModel1Tests) ... FAIL
test_values (custom_model_fields.tests.TestModel1Tests) ... FAIL
test_custom_model_field (custom_model_fields.tests.TestModel2Tests) ... FAIL
test_values (custom_model_fields.tests.TestModel2Tests) ... FAIL

1.8.x mit Py2:

Code: Alles auswählen

test_custom_model_field (custom_model_fields.tests.TestModel1Tests) ... ok
test_values (custom_model_fields.tests.TestModel1Tests) ... ok
1.8.x mit Py3:

Code: Alles auswählen

test_custom_model_field (custom_model_fields.tests.TestModel1Tests) ... ok
test_values (custom_model_fields.tests.TestModel1Tests) ... ok

master mit Py2: (geht überhaupt nicht!)

Code: Alles auswählen

Traceback (most recent call last):
  File "/home/jens/PyLucid_env/src/django/tests/runtests.py", line 12, in <module>
    from django.apps import apps
  File "/home/jens/PyLucid_env/src/django/django/apps/__init__.py", line 1, in <module>
    from .config import AppConfig   # NOQA
  File "/home/jens/PyLucid_env/src/django/django/apps/config.py", line 6, in <module>
    from django.utils.module_loading import module_has_submodule
  File "/home/jens/PyLucid_env/src/django/django/utils/module_loading.py", line 4, in <module>
    from importlib import import_module
  File "/home/jens/PyLucid_env/src/django/django/utils/importlib.py", line 6, in <module>
ImportError: cannot import name RemovedInDjango19Warning
master mit Py3:

Code: Alles auswählen

test_custom_model_field (custom_model_fields.tests.TestModel1Tests) ... ok
test_values (custom_model_fields.tests.TestModel1Tests) ... ok
test_custom_model_field (custom_model_fields.tests.TestModel2Tests) ... ok
test_values (custom_model_fields.tests.TestModel2Tests) ... ok


Also es gibt eigentlich nur ein Problem mit 1.7.x...

Mit 1.8.x muß man auf __metaclass__ = models.SubfieldBase verzichten, denn ansonsten geht nichts:

Code: Alles auswählen

Testing against Django installed in '/home/jens/PyLucid_env/src/django/django'
Importing application custom_model_fields
Traceback (most recent call last):
  File "/home/jens/PyLucid_env/src/django/tests/runtests.py", line 448, in <module>
    options.debug_sql)
  File "/home/jens/PyLucid_env/src/django/tests/runtests.py", line 235, in django_tests
    state = setup(verbosity, test_labels)
  File "/home/jens/PyLucid_env/src/django/tests/runtests.py", line 214, in setup
    apps.set_installed_apps(settings.INSTALLED_APPS)
  File "/home/jens/PyLucid_env/src/django/django/apps/registry.py", line 324, in set_installed_apps
    self.populate(installed)
  File "/home/jens/PyLucid_env/src/django/django/apps/registry.py", line 108, in populate
    app_config.import_models(all_models)
  File "/home/jens/PyLucid_env/src/django/django/apps/config.py", line 198, in import_models
    self.models_module = import_module(models_module_name)
  File "/usr/lib/python2.7/importlib/__init__.py", line 37, in import_module
    __import__(name)
  File "/home/jens/PyLucid_env/src/django/tests/custom_model_fields/models.py", line 41, in <module>
    class CommaSeparatedModelField2(CommaSeparatedModelField1):
  File "/home/jens/PyLucid_env/src/django/django/db/models/fields/subclassing.py", line 22, in __new__
    RemovedInDjango20Warning)
django.utils.deprecation.RemovedInDjango20Warning: SubfieldBase has been deprecated. Use Field.from_db_value instead.
Aber vielleicht ist das nur in den unittests so und normalerweise gibt es nur eine normal Warnung?

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

So, Problem gelöst:


Einmal zu dem Problem mit .values(...): Dazu existiert das Ticket to_python not called when fetching data with .values(...) -> https://code.djangoproject.com/ticket/9619

Also bekannter Bug. Hab ich erstmal vermerkt: https://github.com/jedie/django-dbprefe ... 4ee556ce75


Dann das Problem, das es teilweise nicht mit Py3 funktioniert: Das liegt an der geänderten __metaclass__ Syntax.
Lösung:

Code: Alles auswählen

@six.add_metaclass(models.SubfieldBase)
class FooBarModelField(models.Field):
    def __init__(...)
        ...
-> https://github.com/jedie/django-dbprefe ... 03f67003f6

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Antworten