I have a question regarding Django to which I already have a work-around; but I simply find it hard to believe that this is the actual idiom, so I'm very much open to better suggestions.
Consider de following models:
class Experiment(models.Model):
not_null = models.ForeignKey("self", null=False)
nullable = models.ForeignKey("self", null=True)
And the following REPL-session:
>>> Experiment().not_null is None
Traceback (most recent call last):
File "<console>", line 1, in <module>
[..]
RelatedObjectDoesNotExist: Experiment has no not_null.
>>> Experiment().not_null_id is None
True
>>> Experiment().nullable is None
True
My concern is about the first line of the REPL-session failing. I'd like to be able to check for proper initialization, but I cannot do so idiomatically.
My surprise is about the following: Django raises a DB-related exception (suggesting that there was actually an attempt to look this up in the DB); whereas there would be no need to actually go to the DB to figure out the field is not set. I don't want to be forced to catch an exception that shouldn't have been raised in the first place.
As evidenced, you can work around this by postpending _id, but this is a hack IMHO.
now for the question:
Is there a more idiomatic way to figure out whether a non-nullable field has already been set on newly created object?
I cannot find this in the documentation.
Related
I have a postgres Database and a model with a field as blank=False and null=True.
Let's say:
class MyModel(models.Model):
param1 = models.CharField(max_length=1024)
param2 = models.CharField(max_length=1024)
info = models.CharField(max_length=1024, blank=False, null=False)
Now, when I am creating a model like this:
m = MyModel(param1=val1, param2=val2)
it basically won't raise any exception for info field on saving. Even more, it will keep an empty value for info in the database after using save method.
UPDATED
When instantiating the model like this:
m = MyModel(param1=val1, param2=val2, info=None)
saving will raise an exception in that case
Any suggestions why does it happen? In my opinion if I miss to add a value in the model initialization, it should be at least assumed as None, but it's not. I googled that and couldn't find an specific answer for that. But, found that only full_clean() model method performs checking and raises exceptions like these:
ValidationError: {'info': ['This field cannot be blank.'], 'owner': ['This field cannot be blank.']}
Any help is welcome!
So after researching I came up with this answer:
First of all, blank stands only for form validation, and null for DB validation (docs). As well, Django always assume the empty value for a missing parameter, but with a little bit different behavior for those field types.
The key difference is that for:
Char fields - it uses an empty string (which is ''), the default Django implementation. So if Django sees that there is a missing parameter, it won't pass None for that type of field, since NULL are not being recommended to be stored in DB for char fields. ( details here )
Other fields - it uses an empty value (which is None). So Django sees that there is a missing parameter, it will assume it's None. But since the database restriction null=False, it will raise the exception.
So the conclusion is that only non-charfields that are being supposed to use database constraint null=False - are being checked on save method for missing params by raising exceptions.
Now if you wanna raise exceptions for Charfields as well, you need to use full_clean (used by forms) that will tell you what type of field can't be blank (or missing). So in the end, the right way is to use blank=False and then full_clean to detect missing Charfields. Or/And, you can override the model`s clean() method if have some additional logic to those fields.
info = models.CharField('Facts and features', max_length=1024)
I have a Django class as follows:
class MyModel(models.Model):
my_int = models.IntegerField(null=True, blank=True,)
created_ts = models.DateTimeField(default=datetime.utcnow, editable=False)
When I run the following queryset, I get an error:
>>> from django.db.models import Max, F, Func
>>> MyModel.objects.all().aggregate(Max(Func(F('created_ts'), function='UNIX_TIMESTAMP')))
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "MyVirtualEnv/lib/python2.7/site-packages/django/db/models/query.py", line 297, in aggregate
raise TypeError("Complex aggregates require an alias")
TypeError: Complex aggregates require an alias
How do I adjust my queryset so that I don't get this error?
I want to find the instance of MyModel with the latest created_ts. And I want to use the UNIX_TIMESTAMP function to get it.
I'm not running the same Django version, but I think this'll work:
MyModel.objects.all().aggregate(latest=Max(Func(F('created_ts'), function='UNIX_TIMESTAMP')))
Note the latest keyword argument in there. That's the key (I think, again I can't test this).
From the django docs:
aggregate() is a terminal clause for a QuerySet that, when invoked,
returns a dictionary of name-value pairs
It will automatically provide this value in many generic cases, e.g.
Sum('items') -> sum_items
For your query, it cannot create a default alias, so you must provide one. This is so the query can return a named result for the values produced. All you have to do is give your aggregate a meaningful named alias and all should work fine:
MyModel.objects.all().aggregate(max_created=Max(Func(F('created_ts'), function='UNIX_TIMESTAMP')))
just need set name for result variable :
see item in below code
result = MyModel.objects.all().aggregate(item=Max(Func(F('created_ts'), function='UNIX_TIMESTAMP')))
result.get('item')
Supposing I have a model in Django with a manytomany field.
class MyModel(models.Model):
m2m_field = models.ManyToMany(OtherModel)
class OtherModel(models.Model):
some_text = models.CharField(max_length=256)
If I list MyModel's attributes, there is no m2m_field until I save it.
>>> my_model.m2m_field
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/Library/Python/2.7/site-packages/django/db/models/fields/related.py", line 897, in __get__
through=self.field.rel.through,
File "/Library/Python/2.7/site-packages/django/db/models/fields/related.py", line 586, in __init__
(instance, source_field_name))
ValueError: "<MyModel>" needs to have a value for field "mymodel" before this many-to-many relationship can be used.
Okay, fair enough. So I call my_model.save(), and then I get a RelatedManager object on my_model.m2m_field and I can call my_model.m2m_field.add(other_model_instance).
But.... is there any way to do this in advance? Sometimes it's not so great to post something without certain fields.
I am not totally sure about your question but I think you want to look into post save on the dependent object:
http://www.djangofoo.com/tag/post_save
The idea being that you can save an OtherModel automagically whenever a MyModel is saved?
Feel free to clarify if I have missed your point.
I've added a model boolean field called 'is_dotcom' to my admin list_display, and the implementation is:
email = models.EmailField(max_length=254)
def is_dotcom(self):
return self.email.lower().endsWith(".com")
is_dotcom.admin_order_field = 'email'
is_dotcom.boolean = True
is_dotcom.short_description = 'Company?'
But all this yields on my admin page is "(None)". I'm expecting True/False (though sometimes my booleans show as a green check or red no entry sign, anyone know why that is?)
I've based this code on an example in the django tutorial.
I'm assuming that "(None)" is being shown because the is_dotcom() method is raising an AttributeError which django is catching. I'm guessing that it's legal to call .lower() on an EmailField but I don't know for sure (what do you guys do for reference documentation?) Thanks.
The problem is in this line:
return self.email.lower().endsWith(".com")
The method is .endswith().
Please note the absence of camel-case.
A simplified example which reproduces the error:
>>> 'foo'.endsWith('test')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'endsWith'
Let's say I have a CarModel(models.Model) that uses price = models.DecimalField():
import CarModel
car = CarModel(name='Corvette', price='Hello World')
Traceback (most recent call last):
...
ValidationError: 'price needs to be a Decimal()'
The error was basically that.
Since the price was expecting a decimal but received a string, it raises a ValidationError immediately.
I was expecting it to work like this:
car = CarModel(name='Corvette', price='Hello World')
car.full_clean()
Traceback (most recent call last):
...
ValidationError: 'price needs to be a Decimal()'
Im I thinking about this correctly? Does it seem like ValidationError should only be raised on save() or by explicitly calling full_clean()?
Instances are not saved until you do save(). That being said the issue is one init django tries to do proper coercion, turning all your values into the right datatypes. In this case the data can't even be coerced properly, so Django gives you an exception right away. I realize the distinction may seem a bit nebulous, but if it wasn't this way you could be getting a DecimalField off of a model without getting a Decimal object.