Is this an error in the documentation? - django

Today I started reading the documentation for django.forms. The API seems easy to use and I started experimenting with it. Then I started experimenting with django.forms.ModelForm but I cannot really see where I went wrong.
My problem starts here: the save method when creating a form with an instance.
My model is
class Process(models.Model):
key = models.CharField(max_length=32, default="")
name = models.CharField(max_length=30)
path = models.CharField(max_length=215)
author = models.CharField(max_length=100)
canparse = models.NullBooleanField(default=False)
last_exec = models.DateTimeField(null = True)
last_stop = models.DateTimeField(null = True)
last_change = models.DateTimeField(null = True, auto_now=True)
and my form is
class ProcessForm(ModelForm):
class Meta:
model = Process
fields = ('name', 'path', 'author')
I only wanted the name, path and author fields since the other ones are automatically set when saving the model. Anyway, in my test database I already have entries and I've chosen one whose fields are all set and valid.
In the documentation you can read:
# Create a form to edit an existing Article.
>>> a = Article.objects.get(pk=1)
>>> f = ArticleForm(instance=a)
>>> f.save()
Very well, I wanted to do the same with my own code:
>>> from remusdb.models import Process
>>> from monitor.forms import ProcessForm
>>>
>>> proc = Process.objects.get(name="christ")
>>> pf = ProcessForm(instance=proc)
>>> pf.save()
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/home/shaoran/devpython/lib/python2.6/site-packages/django/forms/models.py", line 364, in save
fail_message, commit, construct=False)
File "/home/shaoran/devpython/lib/python2.6/site-packages/django/forms/models.py", line 87, in save_instance
save_m2m()
File "/home/shaoran/devpython/lib/python2.6/site-packages/django/forms/models.py", line 78, in save_m2m
cleaned_data = form.cleaned_data
AttributeError: 'ProcessForm' object has no attribute 'cleaned_data'
>>> pf.is_bound
False
>>> pf.is_valid()
False
Even though proc is a valid Process object the form object doesn't seem to agree with me. If I do as the next example
>>> post = { "name": "blabla", "path": "/somewhere", "author": "me" }
>>> pf = ProcessForm(post, instance=proc)
>>> pf.is_bound
True
>>> pf.is_valid()
True
>>> pf.cleaned_data
{'path': u'/somewhere', 'name': u'blabla', 'author': u'me'}
then it works like in third example of the documentation.
Am I missing something or is there an error in the documentation? Or is my Model code somewhat wrong?
This is the content of proc
proc.dict
{'name': u'christ', 'last_stop': datetime.datetime(2012, 10, 5, 16, 49, 13, 630040, tzinfo=), 'author': u'unkown', '_state': , 'canparse': False, 'last_exec': datetime.datetime(2012, 10, 5, 16, 49, 8, 545626, tzinfo=), 'key': u'aed72c9d46d2318b99ffba930a110610', 'path': u'/home/shaoran/projects/cascade/remusdb/test/samples/christ.cnf', 'last_change': datetime.datetime(2012, 10, 5, 16, 49, 13, 631764, tzinfo=), 'id': 5}

The first argument to the form class is a dictionary that contains values that you want the form to validate.
Since you never pass these values in, the form cannot validate any input; which is why cleaned_data is none. Since .save() triggers form and model validation, the form validation fails.
You'll notice the form actually has no data:
af.data will be {} (empty dict)
af.is_bound will be False (as you haven't bound the form to any data)
Since there is no data, validation fails. The error is a bit misleading. If you pass in an empty dict:
af = ArticleForm({},instance=a)
af.save()
You'll get a more appropriate error:
ValueError: The Article could not be changed because the data didn't validate.

Related

Django: Strange behaviour: whenever a datetime object is passed it is stored as tuple. Not happens always

I am experiencing a strange behaviour in my views.py file:
in my user model i have
date_something = models.DateTimeField(blank=True, null=True)
The below is the code related in views.py
match = User.objects.get(email='test#test.com')
time_now = datetime.datetime.now(tz=pytz.timezone("UTC"))
match.date_something = time_now,
match.date_something2 = time_now,
# just want to check whats is stored from above line
check = match.date_something
locals()['check']
output: in the runserver console
(datetime.datetime(2019, 10, 10, 8, 20, 10, 787399, tzinfo=<UTC>),)
So here we see that the check is a tuple.
Because of this when i am trying to save the model
match.save()
it says TypeError: expected string or bytes-like object
I checked this code in dango Shell_plus and it gives the right results.
In [2]: import datetime
...: import pytz
...:
...: match = User.objects.get(email='test#test.com')
...: match.date_something = datetime.datetime.now(tz=pytz.timezone("UTC"))
...: check = match.date_something
...:
...: locals()['check']
Out[2]: datetime.datetime(2019, 10, 10, 8, 25, 59, 533597, tzinfo=<UTC>)
FOUND THE PROBLEM
I was adding a comma at the end of that line in the views.py that's why it saving as a tuple.
match.date_something = time_now,

How to test clean_<fieldname> method?

I try to write a test for my clean_ method.
Here is the code for my test
def test_clean_restraints(self):
form = NewTaskForm(dict(restraints="90 20 <>"))
form.clean_restraints()
At this step I receive an error:
Error
Traceback (most recent call last):
File "/home/user/django_projects/my_webservice/tasks/tests/test_forms.py", line 12, in test_clean_restraints
form.clean_restraints()
File "/home/user/django_projects/my_webservice/tasks/forms.py", line 22, in clean_restraints
if self.cleaned_data.get('restraints') == '':
AttributeError: 'NewTaskForm' object has no attribute 'cleaned_data'
NewTaskForm looks like this:
class NewTaskForm(ModelForm):
class Meta:
model = Task
restraints = forms.CharField()
region = forms.CharField()
interactions = forms.CharField()
def clean_restraints(self):
if self.cleaned_data.get('restraints') == '':
return self.cleaned_data.get('restraints')
data = self.cleaned_data.get('restraints').strip().split('\n')
regexp = re.compile(r'^(\d+)[\t ]+(\d+)[ \t]+([><]{2})?$')
cleaned_data = []
for i, line in enumerate(data):
match = regexp.match(line)
if not match:
raise forms.ValidationError(f"Error in restraints in line {i + 1}")
else:
rst_1, rst_2, loop_type = match.groups()
rst_1 = int(rst_1)
rst_2 = int(rst_2)
cleaned_data.append((rst_1, rst_2, loop_type))
return cleaned_data
I'm using Django 2.1, python 3.7.1, PyCharm 2018.3.3 Professional
I tried to run it under debugger in PyCharm but things goes crazy. I receive different error message. It looks like debugger stopped after full form validation ignoring breakpoints. I have no idea what is going on.
You should test the results of the validation process.
form = NewTaskForm(dict(restraints="90 20 <>"))
self.assertFalse(form.is_valid())
self.assertEqual(form.errors['restraints'], "Error in restraints in line 1")
Ok, I found what was wrong.
form.cleaned_data is created in full_clean(). Not in constructor as I thought. It also calls every clean_fieldname(). So the ugly workaround is something like this:
def test_clean_restraints(self):
initial_data = dict(restraints="90 20 <>")
form = NewTaskForm()
form.cleaned_data = initial_data
form.clean_restraints()
(...)

TypeError: Object not iterable - Django create/get

I've been encountering this issue for some time today, and a permanent solution would be helpful.
I have a Vendor object defined as
class Vendors(models.Model):
vendor_id = models.IntegerField(primary_key=True)
vendor_name = models.CharField(max_length=50L)
demo_vendor = models.IntegerField()
url = models.CharField(max_length = 100L, null = True)
logo = models.CharField(max_length = 100L, null = True)
date_created = models.DateTimeField()
class Meta:
db_table = 'txn_vendors'
In a view, I'm implementing a form to allow for creating new vendors along with updating existing ones. To do this, I had to implement a getVendorData method, which required serializing the data in JSON before sending to the client.
serialized = serializers.serialize("json", Vendors.objects.filter(vendor_id = incomingData["id"]))
In that view, if I use filter in place of get, I don't get a TypeError : Vendors is not iterable message.
To implement create and update functionality in one method, I wrote
def saveVendorData(request):
incomingData = simplejson.loads(request.POST.get("data", None))
if incomingData is not None:
vendor = None
newVendor = False
if incomingData["id"] == "":
vendor = Vendors.objects.create(vendor_name = incomingData["vendor_name"], demo_vendor = False, date_created = datetime.now(), url = incomingData["vendor_url"], logo = None)
newVendor = True
else:
vendor = Vendors.objects.get(vendor_id = incomingData["id"])
vendor.vendor_name = incomingData["vendor_name"]
vendor.url = incomingData["vendor_url"]
vendor.save()
serialized = serializers.serialize("json", vendor)
return HttpResponse(simplejson.dumps({"success" : "true", "new_vendor" : newVendor, "data" : serialized}), mimetype = "application/json")
else:
return HttpResponse(simplejson.dumps({"success" : "false", "message" : "Issue with data reception in server"}))
When I try to create a new Vendors object with valid data, I get the TypeError response detailed above and generating this stacktrace:
Vendor Data: [{"pk": 5, "model": "app.vendors", "fields": {"url": "nurturing seniors", "demo_vendor": 1, "vendor_name": "NURTURING SENIORS", "date_created": null, "logo": null}}]
[15/Aug/2013 20:38:01] "POST /getVendorData/ HTTP/1.1" 200 218
Internal Server Error: /saveVendorData/
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py", line 115, in get_response
response = callback(request, *callback_args, **callback_kwargs)
File "/media/Storage/code_projects/Rowdmap_Uboat/app/taxonomy_views.py", line 272, in saveVendorData
serialized = serializers.serialize("json", vendor)
File "/usr/local/lib/python2.7/dist-packages/django/core/serializers/__init__.py", line 99, in serialize
s.serialize(queryset, **options)
File "/usr/local/lib/python2.7/dist-packages/django/core/serializers/base.py", line 42, in serialize
for obj in queryset:
TypeError: 'Vendors' object is not iterable
I need to send this data to the client, since creating a new Vendors record results in the insertion of a new HTML element in several locations in the view.
That error happens because the second param of serialize method is a QuerySet.
See here
get returns a Model instande, but filter returns a QuerySet
The arguments to the serialize function are the format to serialize
the data to (see Serialization formats) and a QuerySet to serialize.
You could write something like:
serialized = serializers.serialize("json", [vendor])

Why can't I set the primary key in a django-dynamic-fixture fixture?

I have the following code:
class SampleModel(models.Model):
sample_model_id = models.AutoField(primary_key=True)
some_date = models.TextField()
def some_function():
s = G(SampleModel, sample_model_id=1234, some_data='abcd')
assert s.sample_model_id == 1
assert s.some_data == 'abcd'
The assert statements pass (they are true statements). Any idea why I can't set the sample_model_id?
I am using Python 2.7, Django 1.4.5, and django-dynamic-fixture 1.6.5 (the latest version).
class SampleModel(models.Model):
some_data = models.TextField()
If I use above class (using automatic id field), I get expected result.
>>> from django_dynamic_fixture import G, get
>>> s = G(SampleModel, id=1234, some_data='abcd')
>>> s.id
1234
>>> s.some_data
'abcd'
With Sample model given in the question, get same result with the question.
Specifying id instead of sample_model_id raises an exception.
>>> s = G(SampleModel, id=1234, some_data='abcd')
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/home/falsetru/.virtualenvs/django15/local/lib/python2.7/site-packages/django_dynamic_fixture/__init__.py", line 107, in get
return d.get(model, shelve=shelve, **kwargs)
File "/home/falsetru/.virtualenvs/django15/local/lib/python2.7/site-packages/django_dynamic_fixture/ddf.py", line 507, in get
instance = self.new(model_class, shelve=shelve, named_shelve=named_shelve, **kwargs)
File "/home/falsetru/.virtualenvs/django15/local/lib/python2.7/site-packages/django_dynamic_fixture/ddf.py", line 436, in new
self.set_data_for_a_field(model_class, instance, field, persist_dependencies=persist_dependencies, **configuration)
File "/home/falsetru/.virtualenvs/django15/local/lib/python2.7/site-packages/django_dynamic_fixture/ddf.py", line 348, in set_data_for_a_field
data = self._process_field_with_default_fixture(field, model_class, persist_dependencies)
File "/home/falsetru/.virtualenvs/django15/local/lib/python2.7/site-packages/django_dynamic_fixture/ddf.py", line 335, in _process_field_with_default_fixture
data = self.data_fixture.generate_data(field)
File "/usr/lib/python2.7/code.py", line 216, in interact
sys.ps2
UnsupportedFieldError: polls.models.SampleModel.sample_model_id
UPDATE
Work around
Specify both id and sample_model_id.
>>> s = G(SampleModel, id=1234, sample_model_id=1234, some_data='abcd')
>>> s.sample_model_id
1234
>>> s.some_data
'abcd'
Actually, id value is not used internally; You can specify any value for id.
>>> s = G(SampleModel, id=None, sample_model_id=5555, some_data='abcd')
>>> s.sample_model_id
5555
>>> s.some_data
'abcd'

Django select_related with fields specified breaks over multiple one to one relationships

I'm getting a weird error trying to select_related over multiple OneToOneField relationships, e.g. in the case where the target field is a grandchild subclass. I'd love someone to help me understand what's going on (or confirm that this is a bug in Django).
Illustration:
# models.py
from django.db import models
class A(models.Model):
pass
class B(A):
pass
class C(B):
pass
Simple enough, right? Now I open the Django shell with a clean database:
>>> C().save()
>>> A.objects.select_related('b__c')
[]
Wait, what? Why is that queryset empty? A quick sanity check:
>>> A.objects.select_related('b')[0].b.c
<C: C object>
So why doesn't the select_related call work? Well, watch this:
>>> A.objects.select_related('b__c').__iter__().next()
...
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/opt/webapps/asdf/lib/python2.6/site-packages/django/db/models/query.py", line 107, in _result_iter
self._fill_cache()
File "/opt/webapps/asdf/lib/python2.6/site-packages/django/db/models/query.py", line 772, in _fill_cache
self._result_cache.append(self._iter.next())
File "/opt/webapps/asdf/lib/python2.6/site-packages/django/db/models/query.py", line 273, in iterator
for row in compiler.results_iter():
File "/opt/webapps/asdf/lib/python2.6/site-packages/django/db/models/sql/compiler.py", line 680, in results_iter
for rows in self.execute_sql(MULTI):
File "/opt/webapps/asdf/lib/python2.6/site-packages/django/db/models/sql/compiler.py", line 725, in execute_sql
sql, params = self.as_sql()
File "/opt/webapps/asdf/lib/python2.6/site-packages/django/db/models/sql/compiler.py", line 58, in as_sql
self.pre_sql_setup()
File "/opt/webapps/asdf/lib/python2.6/site-packages/django/db/models/sql/compiler.py", line 29, in pre_sql_setup
self.fill_related_selections()
File "/opt/webapps/asdf/lib/python2.6/site-packages/django/db/models/sql/compiler.py", line 661, in fill_related_selections
used, next, restricted, new_nullable)
File "/opt/webapps/asdf/lib/python2.6/site-packages/django/db/models/sql/compiler.py", line 617, in fill_related_selections
chain = opts.get_base_chain(f.rel.to)
File "/opt/webapps/asdf/lib/python2.6/site-packages/django/db/models/options.py", line 452, in get_base_chain
% model._meta.module_name,)
TypeError: 'b' is not an ancestor of this model
>>>
So, is this a Django bug, or am I not understanding something?
I added name=CharField to A and ran the following tests in shell:
>>> a = A(name='a_object')
>>> a.save()
>>> b = B(name='b_object')
>>> b.save()
>>> c = C(name='c_object')
>>> c.save()
>>> A.objects.all()
[<A: A object>, <A: A object>, <A: A object>]
>>> B.objects.all()
[<B: B object>, <B: B object>]
>>> C.objects.all()
[<C: C object>]
>>> A.objects.select_related('b__c')
[]
>>> A.objects.select_related('b__c').__iter__().next()
Traceback ....
...
TypeError: 'b' is not an ancestor of this model
>>> d = A.objects.select_related('B__C')
>>> for item in d:
... print item.name
...
a_object
b_object
c_object
>>> test = A.objects.select_related('B__C').__iter__().next()
>>> test.name
u'a_object'
I know it's not an answer, and I don't know what to make of it. But basically I found the lowercase letter don't seem to mean anything without fields in the models.
Got the same problem, but with quite a bit different model structure:
class A(models.Model):
b = models.ForeignKey(B)
....
class B(models.Model):
....
class C(models.Model):
b = models.OneToOneField(B)
d = models.OneToOneField(D)
....
class D(models.Model):
....
So, when I write
A.objects.select_related('b__c__d').all()
I don't see all A - objects, only those where C is not null.
However, when I write
A.objects.select_related('b__c').all()
Django ORM returns me all A objects, including those where C is null.
Seems like a bug to me, select_related should not work as a filter.