【发布时间】:2021-09-06 23:43:01
【问题描述】:
我有两个模型
import uuid
from django.db import models
class Currency(models.Model):
"""Currency model"""
name = models.CharField(max_length=120, null=False,
blank=False, unique=True)
code = models.CharField(max_length=3, null=False, blank=False, unique=True)
symbol = models.CharField(max_length=5, null=False,
blank=False, default='$')
def __str__(self) -> str:
return self.code
class Transaction(models.Model):
uid = models.UUIDField(default=uuid.uuid4, editable=False)
name = models.CharField(max_length=50, null=False, blank=False)
email = models.EmailField(max_length=50, null=False, blank=False)
creation_date = models.DateTimeField(auto_now_add=True)
currency = models.ForeignKey(
Currency, null=False, blank=False, on_delete=models.PROTECT)
payment_intent_id = models.CharField(
max_length=100, null=True, blank=False, default=None)
message = models.TextField(null=True, blank=True)
def __str__(self) -> str:
return f"{self.name} - {self.id} : {self.currency}"
@property
def link(self):
"""
Link to a payment form for the transaction
"""
return f'http://127.0.0.1:8000/payment/{str(self.id)}'
还有三个序列化器
from django.conf import settings
from django.core.validators import (MaxLengthValidator,
ProhibitNullCharactersValidator)
from rest_framework import serializers
from apps.Payment.models import Currency, Transaction
class CurrencySerializer(serializers.ModelSerializer):
class Meta:
model = Currency
fields = ['name', 'code', 'symbol']
if settings.DEBUG == True:
extra_kwargs = {
'name': {
'validators': [MaxLengthValidator, ProhibitNullCharactersValidator]
},
'code': {
'validators': [MaxLengthValidator, ProhibitNullCharactersValidator]
}
}
class UnfilledTransactionSerializer(serializers.ModelSerializer):
currency = serializers.SlugRelatedField(
slug_field='code',
queryset=Currency.objects.all(),
)
class Meta:
model = Transaction
fields = (
'name',
'currency',
'email',
'message'
)
class FilledTransactionSerializer(serializers.ModelSerializer):
currency = serializers.StringRelatedField(read_only=True)
link = serializers.ReadOnlyField()
class Meta:
model = Transaction
fields = '__all__'
extra_kwargs = {
"""Non editable fields"""
'id': {'read_only': True},
'creation_date': {'read_only': True},
'payment_intent_id': {'read_only': True},
}
还有两个视图
from rest_framework.viewsets import ModelViewSet
from apps.Payment.models import Currency, Transaction
from apps.Payment.serializers import (CurrencySerializer,
FilledTransactionSerializer,
UnfilledTransactionSerializer)
class CurrencyViewSet(ModelViewSet):
queryset = Currency.objects.all()
serializer_class = CurrencySerializer
class TransactionViewset(ModelViewSet):
"""Transaction Viewset"""
queryset = Transaction.objects.all()
def get_serializer_class(self):
if self.action == 'create':
return UnfilledTransactionSerializer
else:
return FilledTransactionSerializer
也是一个信号
from django.db.models.signals import post_save
from django.dispatch import receiver
from apps.Payment.models import Transaction
from apps.Payment.utils import fill_transaction
@receiver(post_save, sender=Transaction)
def transaction_filler(sender, instance, created, *args, **kwargs):
''' fill 'payment_intent_id' field in a transacton before saving '''
if created:
fill_transaction(instance)
在下面的代码中,两个测试用例都通过了(因为我在测试时使用了数据库)
class TestUnfilledTransactionSerializer:
def test_serialize_model(self):
transaction = TransactionFactory.build()
expected_serialized_data = {
'name': transaction.name,
'currency': transaction.currency.code,
'email': transaction.email,
'message': transaction.message,
}
serializer = UnfilledTransactionSerializer(transaction)
assert serializer.data == expected_serialized_data
@pytest.mark.django_db
def test_serialized_data(self):
c = CurrencyFactory()
transaction = TransactionFactory.build(currency=c)
valid_serialized_data = {
'name': transaction.name,
'currency': transaction.currency.code,
'email': transaction.email,
'message': transaction.message,
}
serializer = UnfilledTransactionSerializer(data=valid_serialized_data)
assert serializer.is_valid(raise_exception=True)
assert serializer.errors == {}
但是,一旦我从第二个测试用例中删除数据库访问权限,我就会收到此错误。我知道为什么会出现这个错误。
RuntimeError: Database access not allowed, use the "django_db" mark, or the "db" or "transactional_db" fixtures to enable it.
这是工厂的代码
import factory
from apps.Payment.models import Transaction, Currency
from faker import Faker
fake = Faker()
class CurrencyFactory(factory.django.DjangoModelFactory):
class Meta:
model = Currency
# Since currency is declared as a parameter, it won't be passed to
# the model (it's automatically added to Meta.exclude.
class Params:
currency = factory.Faker("currency") # (code, name)
code = factory.LazyAttribute(lambda o: o.currency[0])
name = factory.LazyAttribute(lambda o: o.currency[1])
symbol = '$'
class TransactionFactory(factory.django.DjangoModelFactory):
class Meta:
model = Transaction
# if we do not assign these attributes here then they will remain blank
# currency is auto generated on creation of transaction
currency = factory.SubFactory(CurrencyFactory)
payment_intent_id = None
email = factory.LazyAttribute(lambda _: fake.email())
name = factory.LazyAttribute(lambda _: fake.name())
现在的问题
我知道 Currency 实例已创建并保存在数据库中,因此存在问题。但是如何防止这种情况,因为我不想在单元测试中进行数据库访问,也不能在没有单元测试的情况下离开序列化程序。
【问题讨论】:
标签: django unit-testing django-rest-framework pytest