我正在开发一个 Django 应用程序,其核心功能基于存储相册。我将 Django 1.8.1 与 Python 3 一起使用。
以下是我迄今为止创建的模型:
import uuid
from django.db import models
from django.conf import settings
from django.utils import timezone
# Create your models here.
class Album(models.Model):
"""
Model for an album
"""
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
title = models.CharField(max_length=200)
description = models.TextField()
owner = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True)
def __str__(self):
return self.title
class Photo(models.Model):
"""
Model for a photo
"""
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
album = models.ForeignKey(Album)
datetime = models.DateTimeField(default=timezone.now, blank=True)
image = models.ImageField(upload_to="images", blank=True, null=True)
以下是测试:
from django.test import TestCase
from django.contrib.auth.models import User
from django.utils import timezone
from django.core.files import File
from albums.models import Album, Photo
from unittest import mock
import factory.django
class UserFactory(factory.django.DjangoModelFactory):
"""
Factory for users
"""
class Meta:
model = User
django_get_or_create = ('first_name', 'last_name', 'username','email', 'password',)
first_name = 'Roger'
last_name = 'Smith'
username = 'user'
email = 'user@example.com'
password = 'password'
class AlbumFactory(factory.django.DjangoModelFactory):
"""
Factory for Albums
"""
class Meta:
model = Album
django_get_or_create = ['title', 'description', 'owner']
title = 'My brilliant album'
description = 'An album of extreme awesomeness'
class PhotoFactory(factory.django.DjangoModelFactory):
"""
Factory for Photos
"""
class Meta:
model = Photo
django_get_or_create = ['album', 'datetime', 'image']
datetime = timezone.now()
image = File(open("albums/tests/sample_image.jpg"))
class AlbumTest(TestCase):
"""
Test album model
"""
def test_create_album(self):
# Create user
user = UserFactory()
# Create album
album = AlbumFactory(owner=user)
# Check we can find it
all_albums = Album.objects.all()
self.assertEqual(len(all_albums), 1)
only_album = all_albums[0]
self.assertEqual(only_album, album)
# Check attributes
self.assertEqual(only_album.title, 'My brilliant album')
self.assertEqual(only_album.description, 'An album of extreme awesomeness')
self.assertEqual(only_album.owner, user)
class PhotoTest(TestCase):
"""
Test photo model
"""
def test_create_photo(self):
# Create user
user = UserFactory()
# Create album
album = AlbumFactory(owner=user)
# Create photo
photo = PhotoFactory(album=album)
# Check we can find it
all_photos = Photo.objects.all()
self.assertEqual(len(all_photos), 1)
only_photo = all_photos[0]
self.assertEqual(only_photo, photo)
# Check attributes
self.assertEqual(only_photo.album.pk, album.pk)
self.assertEqual(only_photo.datetime, photo.datetime)
现在,我正在使用 Factory Boy 在测试中创建对象。当我尝试Photo
使用 Factory Boy 创建对象时,出现以下错误:
Python 3.4.0 (default, Apr 11 2014, 13:05:11)
Type "copyright", "credits" or "license" for more information.
IPython 3.1.0 -- An enhanced Interactive Python.
? -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help -> Python's own help system.
object? -> Details about 'object', use 'object??' for extra details.
In [1]: from albums.models import *
In [2]: from albums.tests import *
In [3]: user = UserFactory()
In [4]: album = AlbumFactory(owner=user)
In [5]: photo = PhotoFactory(album=album)
---------------------------------------------------------------------------
DoesNotExist Traceback (most recent call last)
/home/matthew/Projects/myproject/venv/lib/python3.4/site-packages/django/db/models/query.py in get_or_create(self, defaults, **kwargs)
404 try:
--> 405 return self.get(**lookup), False
406 except self.model.DoesNotExist:
/home/matthew/Projects/myproject/venv/lib/python3.4/site-packages/django/db/models/query.py in get(self, *args, **kwargs)
333 "%s matching query does not exist." %
--> 334 self.model._meta.object_name
335 )
DoesNotExist: Photo matching query does not exist.
During handling of the above exception, another exception occurred:
UnicodeDecodeError Traceback (most recent call last)
<ipython-input-5-4b467adb13ef> in <module>()
----> 1 photo = PhotoFactory(album=album)
/home/matthew/Projects/myproject/venv/lib/python3.4/site-packages/factory/base.py in __call__(cls, **kwargs)
79 return cls.build(**kwargs)
80 elif cls._meta.strategy == CREATE_STRATEGY:
---> 81 return cls.create(**kwargs)
82 elif cls._meta.strategy == STUB_STRATEGY:
83 return cls.stub(**kwargs)
/home/matthew/Projects/myproject/venv/lib/python3.4/site-packages/factory/base.py in create(cls, **kwargs)
557 """Create an instance of the associated class, with overriden attrs."""
558 attrs = cls.attributes(create=True, extra=kwargs)
--> 559 return cls._generate(True, attrs)
560
561 @classmethod
/home/matthew/Projects/myproject/venv/lib/python3.4/site-packages/factory/base.py in _generate(cls, create, attrs)
482
483 # Generate the object
--> 484 obj = cls._prepare(create, **attrs)
485
486 # Handle post-generation attributes
/home/matthew/Projects/myproject/venv/lib/python3.4/site-packages/factory/base.py in _prepare(cls, create, **kwargs)
457 )
458 if create:
--> 459 return cls._create(model_class, *args, **kwargs)
460 else:
461 return cls._build(model_class, *args, **kwargs)
/home/matthew/Projects/myproject/venv/lib/python3.4/site-packages/factory/django.py in _create(cls, model_class, *args, **kwargs)
145
146 if cls._meta.django_get_or_create:
--> 147 return cls._get_or_create(model_class, *args, **kwargs)
148
149 return manager.create(*args, **kwargs)
/home/matthew/Projects/myproject/venv/lib/python3.4/site-packages/factory/django.py in _get_or_create(cls, model_class, *args, **kwargs)
136 key_fields['defaults'] = kwargs
137
--> 138 obj, _created = manager.get_or_create(*args, **key_fields)
139 return obj
140
/home/matthew/Projects/myproject/venv/lib/python3.4/site-packages/django/db/models/manager.py in manager_method(self, *args, **kwargs)
125 def create_method(name, method):
126 def manager_method(self, *args, **kwargs):
--> 127 return getattr(self.get_queryset(), name)(*args, **kwargs)
128 manager_method.__name__ = method.__name__
129 manager_method.__doc__ = method.__doc__
/home/matthew/Projects/myproject/venv/lib/python3.4/site-packages/django/db/models/query.py in get_or_create(self, defaults, **kwargs)
405 return self.get(**lookup), False
406 except self.model.DoesNotExist:
--> 407 return self._create_object_from_params(lookup, params)
408
409 def update_or_create(self, defaults=None, **kwargs):
/home/matthew/Projects/myproject/venv/lib/python3.4/site-packages/django/db/models/query.py in _create_object_from_params(self, lookup, params)
437 try:
438 with transaction.atomic(using=self.db):
--> 439 obj = self.create(**params)
440 return obj, True
441 except IntegrityError:
/home/matthew/Projects/myproject/venv/lib/python3.4/site-packages/django/db/models/query.py in create(self, **kwargs)
346 obj = self.model(**kwargs)
347 self._for_write = True
--> 348 obj.save(force_insert=True, using=self.db)
349 return obj
350
/home/matthew/Projects/myproject/venv/lib/python3.4/site-packages/django/db/models/base.py in save(self, force_insert, force_update, using, update_fields)
708
709 self.save_base(using=using, force_insert=force_insert,
--> 710 force_update=force_update, update_fields=update_fields)
711 save.alters_data = True
712
/home/matthew/Projects/myproject/venv/lib/python3.4/site-packages/django/db/models/base.py in save_base(self, raw, force_insert, force_update, using, update_fields)
736 if not raw:
737 self._save_parents(cls, using, update_fields)
--> 738 updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
739 # Store the database on which the object was saved
740 self._state.db = using
/home/matthew/Projects/myproject/venv/lib/python3.4/site-packages/django/db/models/base.py in _save_table(self, raw, cls, force_insert, force_update, using, update_fields)
820
821 update_pk = bool(meta.has_auto_field and not pk_set)
--> 822 result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
823 if update_pk:
824 setattr(self, meta.pk.attname, result)
/home/matthew/Projects/myproject/venv/lib/python3.4/site-packages/django/db/models/base.py in _do_insert(self, manager, using, fields, update_pk, raw)
859 """
860 return manager._insert([self], fields=fields, return_id=update_pk,
--> 861 using=using, raw=raw)
862
863 def delete(self, using=None):
/home/matthew/Projects/myproject/venv/lib/python3.4/site-packages/django/db/models/manager.py in manager_method(self, *args, **kwargs)
125 def create_method(name, method):
126 def manager_method(self, *args, **kwargs):
--> 127 return getattr(self.get_queryset(), name)(*args, **kwargs)
128 manager_method.__name__ = method.__name__
129 manager_method.__doc__ = method.__doc__
/home/matthew/Projects/myproject/venv/lib/python3.4/site-packages/django/db/models/query.py in _insert(self, objs, fields, return_id, raw, using)
918 query = sql.InsertQuery(self.model)
919 query.insert_values(fields, objs, raw=raw)
--> 920 return query.get_compiler(using=using).execute_sql(return_id)
921 _insert.alters_data = True
922 _insert.queryset_only = False
/home/matthew/Projects/myproject/venv/lib/python3.4/site-packages/django/db/models/sql/compiler.py in execute_sql(self, return_id)
968 self.return_id = return_id
969 with self.connection.cursor() as cursor:
--> 970 for sql, params in self.as_sql():
971 cursor.execute(sql, params)
972 if not (return_id and cursor):
/home/matthew/Projects/myproject/venv/lib/python3.4/site-packages/django/db/models/sql/compiler.py in as_sql(self)
926 ) for f in fields
927 ]
--> 928 for obj in self.query.objs
929 ]
930 else:
/home/matthew/Projects/myproject/venv/lib/python3.4/site-packages/django/db/models/sql/compiler.py in <listcomp>(.0)
926 ) for f in fields
927 ]
--> 928 for obj in self.query.objs
929 ]
930 else:
/home/matthew/Projects/myproject/venv/lib/python3.4/site-packages/django/db/models/sql/compiler.py in <listcomp>(.0)
924 getattr(obj, f.attname) if self.query.raw else f.pre_save(obj, True),
925 connection=self.connection
--> 926 ) for f in fields
927 ]
928 for obj in self.query.objs
/home/matthew/Projects/myproject/venv/lib/python3.4/site-packages/django/db/models/fields/files.py in pre_save(self, model_instance, add)
313 if file and not file._committed:
314 # Commit the file to storage prior to saving the model
--> 315 file.save(file.name, file, save=False)
316 return file
317
/home/matthew/Projects/myproject/venv/lib/python3.4/site-packages/django/db/models/fields/files.py in save(self, name, content, save)
92 args, varargs, varkw, defaults = getargspec(self.storage.save)
93 if 'max_length' in args:
---> 94 self.name = self.storage.save(name, content, max_length=self.field.max_length)
95 else:
96 warnings.warn(
/home/matthew/Projects/myproject/venv/lib/python3.4/site-packages/django/core/files/storage.py in save(self, name, content, max_length)
62 name = self.get_available_name(name)
63
---> 64 name = self._save(name, content)
65
66 # Store filenames with forward slashes, even on Windows
/home/matthew/Projects/myproject/venv/lib/python3.4/site-packages/django/core/files/storage.py in _save(self, name, content)
251 try:
252 locks.lock(fd, locks.LOCK_EX)
--> 253 for chunk in content.chunks():
254 if _file is None:
255 mode = 'wb' if isinstance(chunk, bytes) else 'wt'
/home/matthew/Projects/myproject/venv/lib/python3.4/site-packages/django/core/files/base.py in chunks(self, chunk_size)
83
84 while True:
---> 85 data = self.read(chunk_size)
86 if not data:
87 break
/usr/lib/python3.4/codecs.py in decode(self, input, final)
311 # decode input (taking the buffer into account)
312 data = self.buffer + input
--> 313 (result, consumed) = self._buffer_decode(data, self.errors, final)
314 # keep undecoded input until the next call
315 self.buffer = data[consumed:]
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 0: invalid start byte
如果我尝试手动定义图像,也会发生同样的事情。据我所知,图像的路径是正确的。另外,我对在 Python 2.7 上运行的 Django 1.7 项目使用了类似的东西,没有问题,所以它可能是 Python 3 特有的东西。
谁能看到我在这里出错的地方?我可以使用 Django 管理界面创建新的 Photo 对象,所以我认为问题不在于模型,而在于我在测试中与它交互的方式。