Django ArrayField - Choices from a class of constants - ERROR fields.E005 - python

I have a file with constants declared inside classes:
class LanguageChoices:
EN = "English"
FR = "French"
#classmethod
def choices(cls):
return (
(cls.EN, _("English")),
(cls.FR, _("French")),
)
And my models:
from django.contrib.postgres.fields import ArrayField
from apps.users.constants import LanguageChoices
class Data(models.Model):
language = ArrayField(
models.CharField(
max_length=30, choices=LanguageChoices.choices()),
blank=True, null=True
)
)
When I try to run migrations it run into this error (this happens even if I erase all previous migration files and use a new database):
SystemCheckError: System check identified some issues:
ERRORS:
users.Data.language: (postgres.E001) Base field for array has errors:
'choices' must be an iterable containing (actual value, human readable name) tuples. (fields.E005)
ERROR: 1
Any ideas?

Well, turns out the code is good, there was a little comma after one of the values (like EN = "English",) right in the middle. VSCode pulled no warns and my vision played me some tricks and had hide it from me.

Related

Why is Django giving me a ValueError when I reference a class within the same model?

I'm building a simple recipe app, and so far I have two models: Ingredient and Recipe.
Each recipe should have multiple ingredients, so I laid out my model like this:
class Ingredient(models.Model):
name = models.CharField(max_length=50)
class Recipe(models.Model):
title = models.CharField(max_length=100)
ingredients = models.ForeignKey(Ingredient, on_delete=CASCADE)
instructions = JSONField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=SET_DEFAULT, default='Chef Anon')
When I makemigrations, I get nothing, but when I migrate, I get this ValueError:
ValueError: Cannot alter field cookbook.Recipe.ingredients into cookbook.Recipe.ingredients - they do not properly define db_type (are you using a badly-written custom field?)
Following the example here (Django: Add foreign key in same model but different class), I've tried setting ingredients=models.ForeignKey(Ingredient, on_delete=CASCADE) as well as using lazy syntax ingredients=models.ForeignKey("Ingredient", on_delete=CASCADE), but each time, makemigrations shows no changes, and migrate gives me the same ValueError.
Edit
My imports:
from django.db.models.deletion import CASCADE, SET_DEFAULT, SET_NULL
from django.db.models.fields.json import JSONField
from django.utils import timezone
from django.contrib.auth.models import User```
Try replacing on_delete=CASCADE with on_delete=models.CASCADE
If you have not imported CASCADE separately from models.
All though, in that case you should get a warning that "CASCADE is not defined".
I believe I found the problem: My models.py file was in the root directory, not in the app directory.

Django model string representation [duplicate]

Django's internationalization is very nice (gettext based, LocaleMiddleware), but what is the proper way to translate the model name and the attributes for admin pages? I did not find anything about this in the documentation:
http://docs.djangoproject.com/en/dev/topics/i18n/internationalization/
http://www.djangobook.com/en/2.0/chapter19/
I would like to have "Выберите заказ для изменения" instead of "Выберите order для изменения". Note, the 'order' is not translated.
First, I defined a model, activated USE_I18N = True in settings.py, run django-admin makemessages -l ru. No entries are created by default for model names and attributes.
Grepping in the Django source code I found:
$ ack "Select %s to change"
contrib/admin/views/main.py
70: self.title = (self.is_popup and ugettext('Select %s') % force_unicode(self.opts.verbose_name) or ugettext('Select %s to change') % force_unicode(self.opts.verbose_name))
So the verbose_name meta property seems to play some role here. Tried to use it:
class Order(models.Model):
subject = models.CharField(max_length=150)
description = models.TextField()
class Meta:
verbose_name = _('order')
Now the updated po file contains msgid 'order' that can be translated. So I put the translation in. Unfortunately running the admin pages show the same mix of "Выберите order для изменения".
I'm currently using Django 1.1.1.
Could somebody point me to the relevant documentation? Because google can not. ;-) In the mean time I'll dig deeper into the django source code...
Important things not mentioned in the Django documentation:
run django-admin compilemessages, e.g. as a part of your build
process. Thanks stevejalim!
apply django's ugettext_lazy() to model names ( Meta class and verbose_name )
attribute (model field verbose_name) names can also be translated with ugettext_lazy()
use lazy translation in your model metadata, otherwise the translation
happens while loading the model classes and the settings of your users,
especially the browser settings, will not be taken into account
I use some scoping for attribute names, e.g. separating the model name
and attribute names with a pipe. The same convention is used in
ruby-gettext. Background: attribute names like 'title' or 'name' translated
differently in the most languages depending on context. Example
'Book|title' -> 'Titel' or 'Buchtitel' in German. But
'Chapter|title' would be translated as 'Überschrift'.
Example using above principles:
from django.utils.translation import ugettext_lazy as _
class Order(models.Model):
subject = models.CharField(max_length=150, verbose_name = _('Order|subject'))
description = models.TextField( verbose_name = _('Order|description'))
class Meta:
verbose_name = _('order')
verbose_name_plural = _('orders')
Or is there a better way to translate the model and admin pages?
Either way we should enhance the Django documentation and fill the gap!
See https://automationpanda.com/2018/04/21/django-admin-translations/
It's author made an excellent work in showing how to master all django translation features step by step. It's much better than oficial documentation to me.
I use both ugettext with _ and ugettext_lazy with a double __. Consequently, the makemessages management command was only collecting the first ones.
To collect both _ and __ we can ask the gettext tool to collect more than the default strings enclosed in _( ). To do this, we override the makemessages command: https://docs.djangoproject.com/en/dev/topics/i18n/translation/#customizing-the-makemessages-command
# myapp/management/commands/makemessages.py
from django.core.management.commands import makemessages
class Command(makemessages.Command):
self.stdout.write("----> Using our custom makemessages command to collect both _ and double __")
xgettext_options = makemessages.Command.xgettext_options + ['--keyword=__'] # <-- added __
My models in Admin are now finally fully translated.

Django 2.2 cannot serialize default values once migration has been done

I have a model which is refered to as a foreignkey with on_delete set to SET_DEFAULT. Because of that, I need to provide this model with a default item. I created a static method which does just that.
class ScheduleChoice(models.Model):
"""
This model allows users to define crontab schedules.
"""
label = models.CharField(max_length=256, verbose_name="Label", unique=True)
job_schedule = models.CharField(
max_length=256,
default="0 0 * * *", verbose_name="Crontab"
)
#staticmethod
def get_default_schedule_choice():
"""
Because some models rely on ScheduleChoice and need a default value,
we need to give them a default ScheduleChoice.
"""
try:
choice = ScheduleChoice.objects.get_or_create(
label="Every minute",
job_schedule="* * * * *"
)
except ProgrammingError:
choice = None
return choice
#classmethod
def get_choice_count(cls):
"""
Returns how many schedule choices have been defined.
"""
return len(cls.objects.all())
class Meta:
verbose_name = "schedule choice"
verbose_name_plural = "schedule choices"
def __str__(self):
return self.label
class MyOtherModel(models.Model):
"""
Model using ScheduleChoices.
"""
job_schedule = models.ForeignKey(
"ScheduleChoice",
on_delete=models.SET_DEFAULT,
default=ScheduleChoice.get_default_schedule_choice(),
verbose_name="Schedule"
)
activated = models.BooleanField(default=False, verbose_name="activated")
I am able to run makemigrations and migrate without issue.
My problem starts when I modifiy my models and try to use makemigrations again in order to update the migrations files. I get the error :
ValueError: Cannot serialize: <ScheduleChoice: Every minute>
There are some values Django cannot serialize into migration files.
For more, see https://docs.djangoproject.com/en/2.2/topics/migrations/#migration-serializing
I tried to apply this answer, but it didn't help. Why does Django need to serialize my default value ? Why does it only do so after the first migration has successfully ended ?
I can always use reset_db and do my migration, but it is not acceptable in my production environment.
How can I fix this ?
Django needs to serialize your models to make migration files for them. Hence it also needs to serialize most of the attributes you set on the model fields (default included). Currently you define a method and directly call that instead of providing the method as the default, also your method returns a tuple with ScheduleChoice and a boolean.
Django can serialize booleans but not the model instance (for the migration) hence you get an error, not to mention the tuple would have caused an error anyway. You should not call the method and instead just pass the method as the default, also instead of returning the instance return only the pk, also ideally this method should simply be a function:
class ScheduleChoice(models.Model):
"""
This model allows users to define crontab schedules.
"""
label = models.CharField(max_length=256, verbose_name="Label", unique=True)
job_schedule = models.CharField(
max_length=256,
default="0 0 * * *", verbose_name="Crontab"
)
#classmethod
def get_choice_count(cls):
"""
Returns how many schedule choices have been defined.
"""
return len(cls.objects.all())
class Meta:
verbose_name = "schedule choice"
verbose_name_plural = "schedule choices"
def __str__(self):
return self.label
def get_default_schedule_choice():
choice, _created = ScheduleChoice.objects.get_or_create(
label="Every minute",
job_schedule="* * * * *"
)
# Why would programming error occur?
return choice.pk # Return the pk
class MyOtherModel(models.Model):
"""
Model using ScheduleChoices.
"""
job_schedule = models.ForeignKey(
"ScheduleChoice",
on_delete=models.SET_DEFAULT,
default=get_default_schedule_choice, # Don't call the function, only pass it
verbose_name="Schedule"
)
activated = models.BooleanField(default=False, verbose_name="activated")

Django TypeError: argument of type 'datetime.datetime' is not iterable

When I try to add a new field or modify my model in any way I get the following error when I try to migrate to the database:
File "C:\Users\uddin\Envs\web\lib\site-packages\django\db\models\fields\__init__.py", line 1913, in get_prep_value if value and ':' in value: TypeError: argument of type 'datetime.datetime' is not iterable
This is what my model.py file looks like
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
from django.urls import reverse
from PIL import Image
class Post(models.Model):
title = models.CharField(max_length=100)
title2 = models.CharField( max_length=100)
content = models.TextField()
content2 = models.TextField(default=None)
post_image = models.ImageField(upload_to='post_pics')
post_image2 = models.ImageField(upload_to='post2_pics')
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk': self.pk})
Full traceback of error
I've added the full traceback in a pastebin as not to make the post too long
front.0008_post_location file
# Generated by Django 3.1.5 on 2021-03-06 01:21
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('front', '0007_post_content'),
]
operations = [
migrations.AddField(
model_name='post',
name='location',
field=models.GenericIPAddressField(default=django.utils.timezone.now),
preserve_default=False,
),
]
Any help would be appreciated
The problem is that at some point you added a GenericIPAddressField named location, and gave as the default a callable that returns a datetime. Running the migration raises an exception since a datetime isn't a valid IP address.
When it didn't work, you just deleted the location field. The problem is that Django's migration system records a history of model changes, so removing it just created a new migration (when you ran makemgirations), keeping the bug forever enshrined in this migration file.
Next time, just fix the problem at the time and remove the buggy migration.
For now, you can simply edit the migration to make that line work. Since a later migration deletes the field, it doesn't really matter how you do that. I would simply replace the default with null=True.
Another option would be to use squashmigrations, which might (or might not) remove these redundant migrations that add and then remove a field. Or, if you're still at an early stage in your program, you can reset your migration history. There are various Stack Overflow questions on that subject.

Django models.py Circular Foreign Key

I have a django app which basically is just a photo album. Right now I have two models: Image and Album. Among other things, each Album has a foreign key to an Image to be its thumbnail and each Image has a foreign key to the Album it belongs in. However, when I try to use manage.py syncdb or manage.py sqlall I get errors saying the class not defined first in models.py isn't defined when it is used in the first class defined.
models.py (abridged):
from django.db import models
import os
class Album(models.Model):
thumb = models.ForeignKey(Image, null=True, blank=True)
class Image(models.Model):
image = models.ImageField(upload_to='t_pics/images')
thumb = models.ImageField(upload_to='t_pics/images/thumbs')
album = models.ForeignKey(Album)
Error I get when I do manage.py sqlall appname:
[...]
File "/path/to/file/appname/models.py", line 4, in ?
class Album(models.Model):
File "/path/to/file/appname/models.py", line 5, in Album
thumb = models.ForeignKey(Image, null=True, blank=True)
NameError: name 'Image' is not defined
I get the same error when I switch the order of the classes in models.py except it says 'Album' undefined instead of 'Image' undefined I also tried commenting the dependancy in the first class then uncommenting after everything else was successfully imported but that didn't help. How should I go about making this work? I'm reluctant to make an entire third class Thumb because it will have a lot of the same code as Image I'm also pretty sure I could manually add the foreign key to the database but I want this to be clean and not hackish.
You don't actually have a circular reference; the issue is that, at the time you define Album, you haven't defined Image yet. You can fix that by using a string instead:
class Album(models.model):
thumb = models.ForeignKey('Image', null=True, blank=True)
However, in this case, you might want to use a OneToOneField instead of a foreign key. (Note that you'll still have to use the trick with the string, though).
Use quotes to force a lazy reference:
models.ForeignKey('Image', null=True, blank=True)
Also, ForeignKey.related_name is your friend (avoids back-reference name clashes).
This is old but anyway, i'd like to say that I don't see a reason for attribute album in model Image. In my opinion, it is not really needed.

Categories