我正在尝试使用pytest对模型进行测试。我在“:memory:”数据库中的SQLite3上使用混合器时遇到麻烦。
我有一个名为Category的模型,该模型具有一个称为“ created_by”的外键属性,该属性引用了我的自定义用户模型。
将User的引用模型分配给mixer.blend()中的category变量时,出现一个异常,指出User实例不是UUID的实例。
请参见下面的测试
import pytest
from mixer.backend.django import mixer
from apps.API import models
pytestmark = pytest.mark.django_db
class TestCategory():
def test_model(self):
category = mixer.blend(models.Category)
assert category.pk == 1, 'Should create a Category instance'
这很奇怪,因为它以前(大约一周前)有效。为什么混合器无法从用户对象的主键强制UUID?
如果有人有任何想法,我会为之倾听。在下面,我定义我的模型并显示一些堆栈跟踪。希望我们能找到一些见识。
这是pytest中的堆栈跟踪:
self = <django.db.models.fields.UUIDField: id>, value = <User: doliver@smith.com>
def to_python(self, value):
if value is not None and not isinstance(value, uuid.UUID):
try:
> return uuid.UUID(value)
venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:2325:
self = <[AttributeError("'UUID' object has no attribute 'int'") raised in repr()] UUID object at 0x7feeebf18f98>, hex = <User: doliver@smith.com>, bytes = None, bytes_le = None
fields = None, int = None, version = None
def __init__(self, hex=None, bytes=None, bytes_le=None, fields=None,
int=None, version=None):
r"""Create a UUID from either a string of 32 hexadecimal digits,
a string of 16 bytes as the 'bytes' argument, a string of 16 bytes
in little-endian order as the 'bytes_le' argument, a tuple of six
integers (32-bit time_low, 16-bit time_mid, 16-bit time_hi_version,
8-bit clock_seq_hi_variant, 8-bit clock_seq_low, 48-bit node) as
the 'fields' argument, or a single 128-bit integer as the 'int'
argument. When a string of hex digits is given, curly braces,
hyphens, and a URN prefix are all optional. For example, these
expressions all yield the same UUID:
UUID('{12345678-1234-5678-1234-567812345678}')
UUID('12345678123456781234567812345678')
UUID('urn:uuid:12345678-1234-5678-1234-567812345678')
UUID(bytes='\x12\x34\x56\x78'*4)
UUID(bytes_le='\x78\x56\x34\x12\x34\x12\x78\x56' +
'\x12\x34\x56\x78\x12\x34\x56\x78')
UUID(fields=(0x12345678, 0x1234, 0x5678, 0x12, 0x34, 0x567812345678))
UUID(int=0x12345678123456781234567812345678)
Exactly one of 'hex', 'bytes', 'bytes_le', 'fields', or 'int' must
be given. The 'version' argument is optional; if given, the resulting
UUID will have its variant and version set according to RFC 4122,
overriding the given 'hex', 'bytes', 'bytes_le', 'fields', or 'int'.
"""
if [hex, bytes, bytes_le, fields, int].count(None) != 4:
raise TypeError('one of the hex, bytes, bytes_le, fields, '
'or int arguments must be given')
if hex is not None:
> hex = hex.replace('urn:', '').replace('uuid:', '')
E AttributeError: 'User' object has no attribute 'replace'
/usr/lib/python3.6/uuid.py:137: AttributeError
During handling of the above exception, another exception occurred:
self = <apps.API.tests.test_models.TestCategory object at 0x7feeef113a58>
def test_model(self):
> category = mixer.blend(models.Category)
apps/API/tests/test_models.py:13:
venv/lib/python3.6/site-packages/mixer/main.py:568: in blend
return type_mixer.blend(**values)
venv/lib/python3.6/site-packages/mixer/main.py:116: in blend
for name, value in defaults.items()
venv/lib/python3.6/site-packages/mixer/main.py:116: in <genexpr>
for name, value in defaults.items()
venv/lib/python3.6/site-packages/mixer/mix_types.py:222: in gen_value
return type_mixer.gen_field(field)
venv/lib/python3.6/site-packages/mixer/backend/django.py:270: in gen_field
return super(TypeMixer, self).gen_field(field)
venv/lib/python3.6/site-packages/mixer/main.py:193: in gen_field
return self.gen_value(field.name, field, unique=unique)
venv/lib/python3.6/site-packages/mixer/main.py:261: in gen_value
return self.get_value(field_name, value)
venv/lib/python3.6/site-packages/mixer/backend/django.py:218: in get_value
return self._get_value(name, value, field)
venv/lib/python3.6/site-packages/mixer/backend/django.py:233: in _get_value
value = field.scheme.to_python(value)
venv/lib/python3.6/site-packages/django/db/models/fields/related.py:874: in to_python
return self.target_field.to_python(value)
self = <django.db.models.fields.UUIDField: id>, value = <User: doliver@smith.com>
def to_python(self, value):
if value is not None and not isinstance(value, uuid.UUID):
try:
return uuid.UUID(value)
except (AttributeError, ValueError):
raise exceptions.ValidationError(
self.error_messages['invalid'],
code='invalid',
> params={'value': value},
)
E django.core.exceptions.ValidationError: ["'doliver@smith.com' is not a valid UUID."]
venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py:2330: ValidationError
请参阅上面的堆栈跟踪中的最后代码部分:
我发现,如果将try块中的“ value”更改为“ value.id”,则不会引发异常。
def to_python(self, value):
if value is not None and not isinstance(value, uuid.UUID):
try:
return uuid.UUID(value)
except (AttributeError, ValueError):
raise exceptions.ValidationError(
self.error_messages['invalid'],
code='invalid',
> params={'value': value},
)
E django.core.exceptions.ValidationError:
["'doliver@smith.com' is not a valid UUID."]
我的用户模型使用UUID作为其主键。
这是我正在使用的两个模型(类别和用户)。
我在Category中的抽象BaseModel继承仅包含基本信息,例如created_date,is_active等。
类别:
# django
from django.conf import settings
from django.db import models
from django.utils.translation import ugettext_lazy as _
# models
from apps.API.models.BaseModel import BaseModel
class Category(BaseModel):
CATEGORIES = (
# choices tuples go here
)
category = models.CharField(_('category'),
choices=CATEGORIES,
max_length=80,
blank=False,
db_index=True
)
created_by = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.SET(
BaseModel.get_sentinel_user),
blank=False,
editable=False,
db_index=True,
related_name=(
'created_categories')
)
class Meta:
app_label = "API"
verbose_name = _('Category')
verbose_name_plural = _('Categories')
def __str__(self):
return '{}'.format(self.category)
这是我的用户模型:
import uuid
# django
from django.contrib.auth.models import PermissionsMixin
from django.contrib.auth.models import AbstractBaseUser
from django.db import models
from django.utils.translation import ugettext_lazy as _
# models
from apps.API.models.BaseModel import BaseModel
from apps.API.manager import UserManager
class User(AbstractBaseUser, PermissionsMixin):
# Use UUID to make the merging of tables much easier if need be.
id = models.UUIDField(primary_key=True,
editable=False,
default=uuid.uuid4,
verbose_name='public identifier',)
email = models.EmailField(_('email address'), unique=True)
first_name = models.CharField(_('first name'),
max_length=255,
blank=True)
last_name = models.CharField(_('last name'),
max_length=255,
blank=True)
created_date = models.DateTimeField(_('created date'),
auto_now_add=True)
is_active = models.BooleanField(_('active'), default=True)
is_superuser = models.BooleanField(_('superuser'), default=False)
is_staff = models.BooleanField(_('staff'), default=False)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
class Meta:
app_label = "API"
verbose_name = _('User')
verbose_name_plural = _('Users')
def __str__(self):
return '{}'.format(self.email)
我跳入Django shell,并且在Mixer中遇到了相同的问题,所以pytest并没有这个问题。
Traceback (most recent call last):
File "/home/users/myname/dev/blah-django-app/venv/lib/python3.6/site-
packages/django/db/models/fields/__init__.py", line 2325, in to_python
return uuid.UUID(value)
File "/usr/lib/python3.6/uuid.py", line 137, in __init__
hex = hex.replace('urn:', '').replace('uuid:', '')
AttributeError: 'User' object has no attribute 'replace'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/home/users/myname/dev/blah-django-app/venv/lib/python3.6/site-packages/mixer/main.py", line 568, in blend
return type_mixer.blend(**values)
File "/home/users/myname/dev/blah-django-app/venv/lib/python3.6/site-packages/mixer/main.py", line 116, in blend
for name, value in defaults.items()
File "/home/users/myname/dev/blah-django-app/venv/lib/python3.6/site-packages/mixer/main.py", line 116, in <genexpr>
for name, value in defaults.items()
File "/home/users/myname/dev/blah-django-app/venv/lib/python3.6/site-packages/mixer/mix_types.py", line 222, in gen_value
return type_mixer.gen_field(field)
File "/home/users/myname/dev/blah-django-app/venv/lib/python3.6/site-packages/mixer/backend/django.py", line 270, in gen_field
return super(TypeMixer, self).gen_field(field)
File "/home/users/myname/dev/blah-django-app/venv/lib/python3.6/site-packages/mixer/main.py", line 193, in gen_field
return self.gen_value(field.name, field, unique=unique)
File "/home/users/myname/dev/blah-django-app/venv/lib/python3.6/site-packages/mixer/main.py", line 261, in gen_value
return self.get_value(field_name, value)
File "/home/users/myname/dev/blah-django-app/venv/lib/python3.6/site-packages/mixer/backend/django.py", line 218, in get_value
return self._get_value(name, value, field)
File "/home/users/myname/dev/blah-django-app/venv/lib/python3.6/site-packages/mixer/backend/django.py", line 233, in _get_value
value = field.scheme.to_python(value)
File "/home/users/myname/dev/blah-django-app/venv/lib/python3.6/site-packages/django/db/models/fields/related.py", line 874, in to_python
return self.target_field.to_python(value)
File "/home/users/myname/dev/blah-django-app/venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py", line 2330, in to_python
params={'value': value},
django.core.exceptions.ValidationError: ["'michaela32@yahoo.com' is not a valid UUID."]
答案 0 :(得分:0)
这似乎是一个版本控制问题,因为Mixer尚不支持Django 2.1。
该PR应该可以解决此问题。由于混音器仓库5个月没有更新,因此不确定何时或是否将其合并。