2016-05-17 7 views
2

Was ich tun möchte, ist schreiben Code, mit dem ich Django-Objektinstanzen aus einer CSV-Datei laden kann. Offensichtlich sollte ich zuerst alle Daten überprüfen, bevor ich etwas speichere.Django Modell Instanz full_clean Methode, ist das richtig?

tl; dr: Die Methode full_clean() fängt keinen bevorstehenden Versuch ein, None in einem Feld ohne null=True zu speichern. Scheint pervers. Ist das Absicht, und wenn ja, warum? Django hat weniger Fehler als alles andere, mit dem ich je gearbeitet habe, also "Bug!" scheint sehr unwahrscheinlich.

Vollversion. Was ich dachte, würde funktionieren, ist für jede Zeile, erstellen Sie eine Objektinstanz, füllen Sie Felder mit den Daten aus der Tabelle und rufen Sie dann die full_clean-Methode auf. I.e. (In Umrissen)

from django.core.exceptions import ValidationError 
... 

# upload a CSV file and open with a csvreader 
errors=[] 
for rownumber, row in enumerate(csvreader): 

    o = SomeDjangoModel() 
    o.somefield = row[0] # repeated for all input data row[1] ... 

    try: 
     reason = "" 
     o.full_clean() 
    except ValidationError as e: 
     reason = "Row:{} Reason:{}".format(rownumber, str(e)) 
     errors.append(reason) 
     # reason, together with the row-number of the csv file, fully explains 
     # what is wrong. 

# end of loop 
if errors: 
    # display errors to the user for him to fix 
else: 
    # repeat the loop, doing .save() instead of .full_clean() 
    # and get database integrity errors trying to save Null in non-null model field. 

Das Problem ist, ist .full_clean() nicht Keine Werte in Feldern fangen ohne null=True

Was soll ich tun? Ideen sind

  1. das Ganze in einer Transaktion wickeln, eine Charge von o.save() innerhalb einer Exception-Handler tun, und rollen Sie die gesamte Transaktion zurück, es sei denn keine Fehler aufgetreten sind. Aber warum sollte die Datenbank gestört werden, wenn wahrscheinlich 90% der Versuche auf triviale Weise ausbrechen?

  2. Fügen Sie die Daten über ein Formular ein, obwohl keine Interaktionen auf Formularebene pro Zeile mit dem Benutzer vorhanden sind.

  3. Manuell auf Keine prüfen, wo es nicht sein sollte. Aber was macht .full_clean nicht?

kann ich verstehen, dass letztlich die einzige Möglichkeit, die Integrität der Datenbank Fehler abzufangen, um zu versuchen, die Daten zu speichern, aber warum Django nicht allein fängt keine in einem Null = False Feld?

BTW Dieses Django 1.9.6

hinzugefügt Detail. Dies ist relevant Felder der Modelldefinition

class OrderHistory(models.Model): 
    invoice_no = models.CharField(max_length=10, unique=True)   # no default 
    invoice_val= models.DecimalField(max_digits=8, decimal_places=2) # no default 
    date  = models.DateField()         # no default 

und das ist, was aus python manage.py shell, getan geschieht, zu zeigen, dass die .full_clean Methode ein

>>> from orderhistory.models import OrderHistory 
>>> from datetime import date 
>>> o = OrderHistory(date=date(2010,3,17), invoice_no="21003163") 
>>> o.invoice_val=None 
>>> o.full_clean() # passes clean 
>>> o.save() # attempt to save this one which has passed full_clean() validation 
Traceback (most recent call last): 
    File "<console>", line 1, in <module> 
    File "/home/nigel/.virtualenvs/edge22/lib/python3.4/site-packages/django/db/models/base.py", line 708, in save 
force_update=force_update, update_fields=update_fields) 
    File "/home/nigel/.virtualenvs/edge22/lib/python3.4/site-packages/django/db/models/base.py", line 736, in save_base 
    updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields) 
    File "/home/nigel/.virtualenvs/edge22/lib/python3.4/site-packages/django/db/models/base.py", line 820, in _save_table 
result = self._do_insert(cls._base_manager, using, fields, update_pk, raw) 
    File "/home/nigel/.virtualenvs/edge22/lib/python3.4/site-packages/django/db/models/base.py", line 859, in _do_insert 
using=using, raw=raw) 
    File "/home/nigel/.virtualenvs/edge22/lib/python3.4/site-packages/django/db/models/manager.py", line 122, in manager_method 
return getattr(self.get_queryset(), name)(*args, **kwargs) 
    File "/home/nigel/.virtualenvs/edge22/lib/python3.4/site-packages/django/db/models/query.py", line 1039, in _insert 
return query.get_compiler(using=using).execute_sql(return_id) 
    File "/home/nigel/.virtualenvs/edge22/lib/python3.4/site-packages/django/db/models/sql/compiler.py", line 1060, in execute_sql 
cursor.execute(sql, params) 
    File "/home/nigel/.virtualenvs/edge22/lib/python3.4/site-packages/django/db/backends/utils.py", line 79, in execute 
return super(CursorDebugWrapper, self).execute(sql, params) 
    File "/home/nigel/.virtualenvs/edge22/lib/python3.4/site-packages/django/db/backends/utils.py", line 64, in execute 
return self.cursor.execute(sql, params) 
    File "/home/nigel/.virtualenvs/edge22/lib/python3.4/site-packages/django/db/utils.py", line 95, in __exit__ 
six.reraise(dj_exc_type, dj_exc_value, traceback) 
    File "/home/nigel/.virtualenvs/edge22/lib/python3.4/site-packages/django/utils/six.py", line 685, in reraise 
raise value.with_traceback(tb) 
    File "/home/nigel/.virtualenvs/edge22/lib/python3.4/site-packages/django/db/backends/utils.py", line 64, in execute 
    return self.cursor.execute(sql, params) 
django.db.utils.IntegrityError: null value in column "invoice_val" violates not-null constraint 
DETAIL: Failing row contains (2, 21003163, , , 2010-03-17, , null, null, null, null, null, null). 
>>> 
>>> p = OrderHistory(invoice_no="21003164") # no date 
>>> p.date=None 
>>> p.full_clean()       # this DOES error as it should 
    Traceback (most recent call last): 
    File "<console>", line 1, in <module> 
    File "/home/nigel/.virtualenvs/edge22/lib/python3.4/site-packages/django/db/models/base.py", line 1144, in full_clean 
    raise ValidationError(errors) 
django.core.exceptions.ValidationError: {'date': ['This field cannot be null.']} 
>>> 
+0

Wie werden None-Werte in Ihrer CSV-Datei dargestellt? Sie haben einen Fehler beim Einrücken des Codes, siehe letzte 4 Zeilen, wo Sie nach Fehlern suchen und speichern. – ozren1983

+0

Das ist kein Codefehler. Ich sammle Fehler während der Schleife. Nach der Schleife, wenn es Fehler gab, zeige ich alle an, andernfalls wiederhole ich die Schleife, die save() anstelle von full_clean() vornimmt. (Dies könnte zu Datenbankintegritätsfehlern führen, aber das ist für diese Frage nicht relevant). – nigel222

+0

Null in CSV-Datei ist zwei Kommas mit nichts zwischen ihnen. Ich behandle auch eine Whitespace-Zeichenfolge in einer numerischen Spalte als null (es ist ein Benutzer, der die Daten spreizt, anstatt sie zu löschen, ein übliches Anti-Pattern für Tabellenkalkulationen). – nigel222

Antwort

0

ich nur Ihre Schritte wiederholt habe zu erkennen versagt in der Schale, und full_clean() löst Validation für Keine Werte:

>>> from orders.models import OrderHistory 
>>> o = OrderHistory() 
>>> o.full_clean() 
Traceback (most recent call last): 
    File "<console>", line 1, in <module> 
    File "/Users/oz/.virtualenvs/full_clean_test/lib/python2.7/site-packages/django/db/models/base.py", line 1144, in full_clean 
    raise ValidationError(errors) 
ValidationError: {'date': [u'This field cannot be null.'], 'invoice_val': [u'This field cannot be null.'], 'invoice_no': [u'This field cannot be blank.']} 

ich habe es auf neues Projekt mit Django getestet 1.9.6 und Python 2.7.10 auf OSX und Pyth auf 3.4.3 auf Ubuntu.

Versuchen Sie, alle * .pyc-Dateien aus Ihrem Projekt zu entfernen. Wenn das nicht funktioniert, entferne dein virtuelles env, erstelle ein neues und installiere deine Abhängigkeiten neu.

+0

Sorry, aber das ist keine Antwort auf die Frage. Ich möchte wissen, warum die full_clean-Methode diese Nullen nicht erkennt. Natürlich kann ich viele Sonderfälle hinzufügen, um die Daten selbst zu überprüfen, aber sollte Django das nicht tun, wenn er weiß, was er über das Modell weiß? – nigel222

+0

Wie Sie selbst sagten, ist es sehr unwahrscheinlich, dass es sich um einen Django oder full_clean() Methodenfehler handelt. Meine Vermutung ist, dass sich Ihr CSV-Reader nicht so verhält, wie Sie es erwarten und dass Ihre leeren Werte (zwei Kommas mit nichts dazwischen) nicht als Python-None-Typ gelesen werden, sondern als leere Strings. Können Sie einige Debug-Logs/Prints hinzufügen und prüfen, welchen Wert und Typ Sie für leere Werte mit print "value: {}, type: {}" erhalten haben format (o.somefield, type (o.somefield))? – ozren1983

+0

Details zur Frage hinzugefügt. Nicht angezeigte Felder haben Standardwerte oder sind null = True. Es ist "o.invoice_val = None", das über full_clean() – nigel222