Why are my model restrictions ignored? - django

here is my model.py :
from django.db import models
class Code(models.Model):
code = models.CharField(max_length=10)
def __str__(self):
return self.code
However when I enter the shell, this returns no mistake :
In [1]: from mini_url.models import Code
In [2]: co = Code()
In [3]: co.save()
In [4]: co = Code("1234567891011")
In [5]: co.save()
Whereas I expect two errors :
1/ from the fact that co is saved without any field, which is supposed to be impossible since there is no null = True
2/ from the fact that I save a Code whosecode field is longer than 10!
Any Idea?

The empty value of a CharField is the empty string ''.
You do not actually set the code field. What your are actually doing is setting the id.
>>> co = Code("1234567891011")
>>> co.id
"1234567891011"
Try:
co = Code(code="1234567891011")
co.save()
or, if you really do not want to use keyword arguments:
co = Code(None, "1234567891011")
It will raise an exception if you use a database that enforces length constraints (some databases, e.g. SQLite, don't).

Related

Django DateTimeRangeField: default=[timezone.now()]-[timezone.now()]+[10YEARS]

I want an "active_in" attribute as a timeframe. I assume that the DBMS is optimized for the postgresql tsrange field, and as such it is preferable to utilize the DateTimeRangeField rather than 2 separate fields for start_date and end_date.
Doing this I desire a default value for the field.
active_in = models.DateTimeRangeField(default=timezone.now+'-'+timezone.now+10YEARS)
Is my assumption about the DateTimeRangeField performance true?
Is there a smart solution be it creating a new; function,class or
simply manipulating the 2nd last digit?
My possible solutions:
Code using string manipulation:
active_in = models.DateTimeRangeField(default=timezone.now+'-'+timezone.now[:-2]+'30')
Code using custom function object: (adjusted from here: https://stackoverflow.com/a/27491426/7458018)
def today_years_ahead():
return timezone.now + '-' timezone.now() + timezone.timedelta(years=10)
class MyModel(models.Model):
...
active_in = models.DateTimeRangeField(default=today_years_ahead)
There's no need for string manipulation, as the documented Python type for this field is DateTimeTZRange.
I can't say I've ever used this field before, but something like this should work:
from psycopg2.extras import DateTimeTZRange
from django.utils import timezone
from datetime import timedelta
def next_ten_years():
now = timezone.now()
# use a more accurate version of "10 years" if you need it
return DateTimeTZRange(now, now + timedelta(days=3652))
class MyModel(models.Model):
...
active_in = models.DateTimeRangeField(default=next_ten_years)

Django. Error saving empty string to DateTimeField(blank=True)

I have a model with a DateTimeField (blank=True, null=True). However, when I try to save an empty string in it, I get the following error:
>>> from courses.models import Event
>>> x = Event.objects.last()
>>> x.date_start = ""
>>> x.save()
django.core.exceptions.ValidationError: ['“” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format.']
I have never seen this, any ideas? All my migrations are up to date.
blank=True [Django-doc] has impact on modelforms, not on the model itself. It does not mean that the element accepts an empty string. blank=True simply means that the field is not required. In case the value is not specified, it will use the default value.
But at the model layer, nothing changes. Since your field has null=True [Django-doc], the default in case you do not fill in the value will be None, so you can use:
>>> from courses.models import Event
>>> x = Event.objects.last()
>>> x.date_start = None
>>> x.save()

Merging models, Django 1.11

After upgrading from Django 1.8 to 1.11 I've been looking at a means of merging some records - some models have multiple entries with the same name field, for example. There's an answer here that would appear to have what I would need:
https://stackoverflow.com/a/41291137/1195207
I tried it with models like this:
class GeneralType(models.Model):
#...
domains = models.ManyToManyField(Domain, blank=True)
#...
class Domain(models.Model):
name = models.TextField(blank=False)
#...
...where Domain has various records with duplicate names. But, it fails at the point indicated:
def merge(primary_object, alias_objects=list(), keep_old=False):
"""
Use this function to merge model objects (i.e. Users, Organizations, Polls,
etc.) and migrate all of the related fields from the alias objects to the
primary object. This does not look at GenericForeignKeys.
Usage:
from django.contrib.auth.models import User
primary_user = User.objects.get(email='good_email#example.com')
duplicate_user = User.objects.get(email='good_email+duplicate#example.com')
merge(primary_user, duplicate_user)
"""
# ...snip....
for alias_object in alias_objects:
for related_object in alias_object._meta.related_objects:
related_name = related_object.get_accessor_name()
if related_object.field.many_to_one:
#...snip...
elif related_object.field.one_to_one:
#...snip...
elif related_object.field.many_to_many:
related_name = related_name or related_object.field.name
for obj in getattr(alias_object, related_name).all():
getattr(obj, related_name).remove(alias_object) # <- fails here
getattr(obj, related_name).add(primary_object)
The problem is apparently that 'GeneralType' object has no attribute 'generaltype_set'. Adding a related_name to GeneralType doesn't fix this - the script fails in the same manner but quoting the name I've now given it. I'm not quite sure what Django is up to here so any suggestions would be welcome.
Edit:
In a Django shell I can successfully reference GeneralType from Domain, so it's something about the script above that I'm not getting. Example:
>>> d = Domain.objects.first()
>>> d
<Domain: 16s RNA>
>>> d.generaltype_set
<django.db.models.fields.related_descriptors.ManyRelatedManager object at 0x11175ba90>
>>> d.generaltype_set.first()
<GeneralType: Greengenes>
>>> getattr(d,'generaltype_set')
<django.db.models.fields.related_descriptors.ManyRelatedManager object at 0x10aa38250>
I managed to come up with a workaround. It seems that everything would function if I referenced generaltype.domains in the getattr(obj, related_name) part of the script, so I modified it as follows just before the line marked as failing in the question above:
if obj.__class__.__name__ == 'GeneralType':
related_name = 'domains'
Everything ran as it should after that, it seems.

Django get_or_create method doesn't return exact object

Here is my models.py:
class Foo(models.Model):
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=30)
In django manage.py shell:
>>> a,b = Foo.objects.get_or_create(name='hi')
>>> b
True
>>> vars(a)
{'_state': <django.db.models.base.ModelState object at 0x02761390>, 'id': None,
'name': 'hi'}
The problem here is the customized id attribute has no value. And if I continue to do the following:
>>> c = foo.objects.get(name='hi')
>>> c.id
1
>>> a is c
False
So that newly created object (denoted by c) already exists now, but b is not an exact reference to it like c is.
Is this a bug? My django version is 1.6.5.
By default, if you have not specified the id field for you model, Django will create AutoField:
id = models.AutoField(primary_key=True)
In your case you've created just IntegerField. I suppose this is the point. In the database, it will still be incremented, but django won't assign the id value from database to the object, returned by get_or_create
By the way, in your question:
>>> b is c
False
b - is a bool value. I think you were trying to do
>>> a is c
But also this is not a good idea. If you want to compare django model objects, use ==, not is:
>>> a == c
You will get False anyway, as a has its id set to None.
You have declared your id field as an IntegerField, so Django does not know it needs to autoincrement. You could define it as an AutoField, but actually there is no reason to declare it at all: Django will automatically define it for you if you leave it out.
Note this has nothing to do with get_or_create: you would have exactly the same problem with a normal create/save.

Django Model.objects.all() and Model.objects.get(pk=?) return two different results

I have a basic model called Restaurant
class Restaurant(models.Model):
place = models.OneToOneField(Place, primary_key=True)
serves_pizza = models.BooleanField()
serves_hotdog = models.BooleanField()
def __unicode__(self):
return u'%s the restaurant' % self.place.name
Querying with Restaurant.objects.all() and Restaurant.objects.get() yields two different results in which only the former is correct.
# this is correct
>>> r=Restaurant.objects.all()
>>> r
[<Restaurant: Hogwarts the restaurant>, <Restaurant: Domino the restaurant>]
>>> r[0].serves_hotdog
True
# this is not correct
>>> r0=Restaurant.objects.get(pk=4556376185503744)
>>> r0.serves_hotdog
False
# although they have the same pk
>>> r0.pk == r[0].pk
True
# their property values are different
>>> r[0].serves_hotdog == r0.serves_hotdog
False
>>> r[0].serves_pizza == r0.serves_pizza
False
Has anyone seen anything similar to this?
If you are using Django-nonrel on GAE, make sure that you don't set primary_key=True for related models because the engine will use the same primary key for both models. For example, in my case, I have Restaurant and Place with OneToOneRelationship. If we use Place as primary key for Restaurant, a restaurant object created from a place object will share the same pk, thus messing up Restaurant.objects.get(pk=).
Dropping the primary key rule fixes my problem:
class Restaurant(models.Model):
place = models.OneToOneField(Place) # no primary_key=True
I can't find this information elsewhere, so I'm posting it here. Hope it helps someone else.