问题
I'm using Django 1.7 and I have a problem with my fixtures.
I would like Django to use the default value or use the save()
method to create unspecified values.
Here are my current objects:
# File: uuidable.py
import uuid
from django.db import models
from django.utils.translation import ugettext_lazy as _
class Uuidable(models.Model):
uuid = models.CharField(_('uuid'), blank=True,
null=False, unique=True,
max_length=64, default=uuid.uuid4()) # Tried here
class Meta:
abstract = True
def save(self, *args, **kwargs):
if self.pk is None:
self.uuid = uuid.uuid4() # Tried here also
super().save(*args, **kwargs)
# File: timestampable.py
from django.db import models
from django.utils.translation import ugettext_lazy as _
class Timestampable(models.Model):
created_at = models.DateTimeField(_('created at'), auto_now_add=True)
updated_at = models.DateTimeField(_('updated at'), auto_now=True)
class Meta:
abstract = True
# File: post.py
from project.lib.models.timestampable import Timestampable
from project.lib.models.uuidable import Uuidable
class Post(Timestampable, Uuidable):
title = models.CharField(_('title'), max_length=250, blank=False)
content = models.TextField(_('content'))
def __str__(self):
return self.title
As you can see, when I generate a new Post()
, the created_at
, updated_at
and uuid
values are automatically created on save()
. But when I use fixtures, I get the following error:
[...]initial_data.yaml': Could not load post.Post(pk=None): UNIQUE constraint failed: post_post.uuid
If I specify a uuid
in my fixture file, then I get an error on created_at
and then on updated_at
. So I have to specify the content of each field, even though I want it to be "automatic".
From the documentation (why is this in the django admin docs ?!), I know that the save()
method is not called so this is why everything I put into the save()
method doesn't work. But shouldn't the default
or auto_now*
features be enables/used ?
When fixture files are processed, the data is saved to the database as is. Model defined save() methods are not called, and any pre_save or post_save signals will be called with raw=True since the instance only contains attributes that are local to the model. You may, for example, want to disable handlers that access related fields that aren’t present during fixture loading and would otherwise raise an exception
Is there a way to "force" Django to automatically use the default
or auto_now*
features for fixtures ? I'm using manage.py syncdb
to create all the tables etc.
I have searched on google and stack overflow but couldn't seem to find the right search keywords.
UPDATE-1: The following google group discussion says that objects are saved in raw
mode, meaning that auto_now*
features are not taken into account. I'm still searching to see if there is a way to hook some model function to the Django fixture saving.
回答1:
The solution was to use django signals:
import uuid
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.db.models.signals import pre_save
from django.dispatch import receiver
class Uuidable(models.Model):
uuid = models.CharField(_('uuid'), blank=True,
null=False, unique=True,
max_length=64, default=uuid.uuid4())
class Meta:
abstract = True
@receiver(pre_save)
def set_uuid_on_save(sender, instance, *args, **kwargs):
if instance.pk is None:
instance.uuid = uuid.uuid4()
That way, the model/data is populated whatever way you create the model (via shell, fixtures, whatever).
回答2:
Automatically loading initial data fixtures is deprecated in Django 1.7. One solution is via signals as you mentioned. Another one that I prefer is to create a python script where you create all the needed data, and execute it in the shell:
python manage.py shell < create_initial_data.py
回答3:
I think that the problem is when you put default=uuid.uuid4()
. The parenthesis are too much, because they imply that you pass the result of uuid.uuid4()
to default argument and not the function itself, so you should put default=uuid.uuid4
.
来源:https://stackoverflow.com/questions/25724318/django-fixtures-save-with-default-value