Added testing for DateField, DateTimeField, and validators on the Model and ModelForm (#23)
* Added testing for DateField and DateTimeField * Update makefile * Adding some of the tests with validators on model * Added ModelForm clean_field and clean tests Co-authored-by: Thu Trang Pham <thu@joinmodernhealth.com>
This commit is contained in:
+15
-2
@@ -1,8 +1,10 @@
|
||||
import factory
|
||||
|
||||
from random import choice, randint
|
||||
from django.utils import timezone
|
||||
|
||||
from tests.market.models import Item, Shop, Inventory
|
||||
from .market.models import Item, Shop, Inventory, Transaction
|
||||
from .market.constants import VALID_CURRENCIES
|
||||
|
||||
|
||||
class ItemFactory(factory.django.DjangoModelFactory):
|
||||
@@ -11,7 +13,7 @@ class ItemFactory(factory.django.DjangoModelFactory):
|
||||
|
||||
name = factory.Faker("name")
|
||||
price = factory.LazyAttribute(lambda _: randint(5, 500))
|
||||
currency = factory.LazyAttribute(lambda _: choice(Item.VALID_CURRENCIES))
|
||||
currency = "CAD"
|
||||
|
||||
|
||||
class ShopFactory(factory.django.DjangoModelFactory):
|
||||
@@ -28,3 +30,14 @@ class InventoryFactory(factory.django.DjangoModelFactory):
|
||||
shop = factory.SubFactory(ShopFactory)
|
||||
item = factory.SubFactory(ItemFactory)
|
||||
quantity = factory.Sequence(lambda n: n)
|
||||
|
||||
|
||||
class TransactionFactory(factory.django.DjangoModelFactory):
|
||||
class Meta:
|
||||
model = Transaction
|
||||
|
||||
currency = "CAD"
|
||||
total = 0
|
||||
date = factory.LazyAttribute(lambda _: timezone.now().date())
|
||||
timestamp = factory.LazyAttribute(lambda _: timezone.now())
|
||||
shop = factory.SubFactory(ShopFactory)
|
||||
|
||||
@@ -1,15 +1,30 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from ..models import GeneralManager, Item, Inventory, Shop, ShoppingMall
|
||||
from ..models import (
|
||||
GeneralManager,
|
||||
Item,
|
||||
Inventory,
|
||||
ItemSale,
|
||||
Shop,
|
||||
ShoppingMall,
|
||||
Transaction,
|
||||
Checkout,
|
||||
)
|
||||
|
||||
from .item_admin import ItemAdmin
|
||||
from .inventory_admin import InventoryAdmin
|
||||
from .shop_admin import ShopAdmin
|
||||
from .shoppingmall_admin import ShoppingMallAdmin
|
||||
from .generalmanager_admin import GeneralManagerAdmin
|
||||
from .item_sale_admin import ItemSaleAdmin
|
||||
from .transaction_admin import TransactionAdmin
|
||||
from .checkout_admin import CheckoutAdmin
|
||||
|
||||
admin.site.register(Item, ItemAdmin)
|
||||
admin.site.register(Inventory, InventoryAdmin)
|
||||
admin.site.register(Shop, ShopAdmin)
|
||||
admin.site.register(ShoppingMall, ShoppingMallAdmin)
|
||||
admin.site.register(GeneralManager, GeneralManagerAdmin)
|
||||
admin.site.register(Transaction, TransactionAdmin)
|
||||
admin.site.register(ItemSale, ItemSaleAdmin)
|
||||
admin.site.register(Checkout, CheckoutAdmin)
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
from django.core.exceptions import ValidationError
|
||||
from admin_confirm.admin import AdminConfirmMixin
|
||||
|
||||
from django.contrib.admin import ModelAdmin
|
||||
from django.forms import ModelForm
|
||||
|
||||
from ..models import Checkout
|
||||
|
||||
|
||||
class CheckoutForm(ModelForm):
|
||||
class Meta:
|
||||
model = Checkout
|
||||
fields = [
|
||||
"currency",
|
||||
"shop",
|
||||
"total",
|
||||
"timestamp",
|
||||
"date",
|
||||
]
|
||||
|
||||
def clean_total(self):
|
||||
try:
|
||||
total = float(self.cleaned_data["total"])
|
||||
except:
|
||||
raise ValidationError("Invalid Total From clean_total")
|
||||
if total == 111: # Use to cause error in test
|
||||
raise ValidationError("Invalid Total 111")
|
||||
|
||||
return total
|
||||
|
||||
def clean(self):
|
||||
try:
|
||||
total = float(self.data["total"])
|
||||
except:
|
||||
raise ValidationError("Invalid Total From clean")
|
||||
if total == 222: # Use to cause error in test
|
||||
raise ValidationError("Invalid Total 222")
|
||||
|
||||
self.cleaned_data["total"] = total
|
||||
|
||||
|
||||
class CheckoutAdmin(AdminConfirmMixin, ModelAdmin):
|
||||
confirm_add = True
|
||||
confirm_change = True
|
||||
autocomplete_fields = ["shop"]
|
||||
form = CheckoutForm
|
||||
@@ -0,0 +1,9 @@
|
||||
from admin_confirm.admin import AdminConfirmMixin
|
||||
|
||||
|
||||
from django.contrib.admin import ModelAdmin
|
||||
|
||||
|
||||
class ItemSaleAdmin(AdminConfirmMixin, ModelAdmin):
|
||||
confirm_add = True
|
||||
confirm_change = True
|
||||
@@ -0,0 +1,9 @@
|
||||
from admin_confirm.admin import AdminConfirmMixin
|
||||
|
||||
|
||||
from django.contrib.admin import ModelAdmin
|
||||
|
||||
|
||||
class TransactionAdmin(AdminConfirmMixin, ModelAdmin):
|
||||
confirm_add = True
|
||||
confirm_change = True
|
||||
@@ -0,0 +1,4 @@
|
||||
VALID_CURRENCIES = (
|
||||
("CAD", "CAD"),
|
||||
("USD", "USD"),
|
||||
)
|
||||
@@ -0,0 +1,45 @@
|
||||
# Generated by Django 3.1.7 on 2021-03-10 23:41
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('market', '0009_auto_20210304_0355'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Transaction',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('timestamp', models.DateField(auto_created=True)),
|
||||
('total', models.DecimalField(decimal_places=2, editable=False, max_digits=5)),
|
||||
('currency', models.CharField(choices=[('CAD', 'CAD'), ('USD', 'USD')], max_length=3)),
|
||||
('shop', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='market.shop')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ItemSale',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('total', models.DecimalField(decimal_places=2, editable=False, max_digits=5)),
|
||||
('currency', models.CharField(choices=[('CAD', 'CAD'), ('USD', 'USD')], max_length=3)),
|
||||
('item', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='market.item')),
|
||||
('transaction', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='item_sales', to='market.transaction')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Checkout',
|
||||
fields=[
|
||||
],
|
||||
options={
|
||||
'proxy': True,
|
||||
'indexes': [],
|
||||
'constraints': [],
|
||||
},
|
||||
bases=('market.transaction',),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,30 @@
|
||||
# Generated by Django 3.1.7 on 2021-03-26 01:30
|
||||
|
||||
from django.db import migrations, models
|
||||
from ..validators import validate_currency
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("market", "0010_checkout_itemsale_transaction"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="itemsale",
|
||||
name="quantity",
|
||||
field=models.PositiveIntegerField(default=1),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="transaction",
|
||||
name="date",
|
||||
field=models.DateTimeField(default=None),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="itemsale",
|
||||
name="currency",
|
||||
field=models.CharField(max_length=5, validators=[validate_currency]),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 3.1.7 on 2021-03-26 02:40
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('market', '0011_auto_20210326_0130'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='transaction',
|
||||
name='date',
|
||||
field=models.DateField(),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='transaction',
|
||||
name='timestamp',
|
||||
field=models.DateTimeField(auto_created=True),
|
||||
),
|
||||
]
|
||||
+52
-4
@@ -1,11 +1,14 @@
|
||||
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
|
||||
from django.db.models.aggregates import Sum
|
||||
from django.db import models
|
||||
from .constants import VALID_CURRENCIES
|
||||
from .validators import validate_currency
|
||||
|
||||
|
||||
class Item(models.Model):
|
||||
VALID_CURRENCIES = (
|
||||
("CAD", "CAD"),
|
||||
("USD", "USD"),
|
||||
)
|
||||
# Because I'm lazy and don't want to update all test references
|
||||
VALID_CURRENCIES = VALID_CURRENCIES
|
||||
|
||||
name = models.CharField(max_length=120)
|
||||
price = models.DecimalField(max_digits=5, decimal_places=2)
|
||||
currency = models.CharField(max_length=3, choices=VALID_CURRENCIES)
|
||||
@@ -57,3 +60,48 @@ class ShoppingMall(models.Model):
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Transaction(models.Model):
|
||||
total = models.DecimalField(max_digits=5, decimal_places=2, default=0)
|
||||
currency = models.CharField(max_length=3, choices=VALID_CURRENCIES)
|
||||
shop = models.ForeignKey(Shop, on_delete=models.CASCADE)
|
||||
timestamp = models.DateTimeField(auto_created=True)
|
||||
date = models.DateField()
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class ItemSale(models.Model):
|
||||
transaction = models.ForeignKey(
|
||||
Transaction, on_delete=models.CASCADE, related_name="item_sales"
|
||||
)
|
||||
item = models.ForeignKey(Item, on_delete=models.SET_NULL, null=True)
|
||||
quantity = models.PositiveIntegerField(default=1)
|
||||
total = models.DecimalField(max_digits=5, decimal_places=2)
|
||||
currency = models.CharField(max_length=5, validators=[validate_currency])
|
||||
|
||||
def clean(self):
|
||||
errors = {}
|
||||
# check that shop has the stock
|
||||
shop = self.transaction.shop
|
||||
inventory = Inventory.objects.filter(shop=shop, item=self.item)
|
||||
if not inventory:
|
||||
errors["item"] = "Shop does not have the item stocked"
|
||||
else:
|
||||
in_stock = inventory.aggregate(Sum("quantity")).get("quantity__sum", 0)
|
||||
if in_stock < self.quantity:
|
||||
errors["item"] = "Shop does not have enough of the item stocked"
|
||||
if errors:
|
||||
raise ValidationError(errors)
|
||||
|
||||
|
||||
class Checkout(Transaction):
|
||||
"""
|
||||
Proxy Model to use in Django Admin to create a Transaction
|
||||
As if a customer was checking out at a physical checkout
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
proxy = True
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
from django.core.exceptions import ValidationError
|
||||
from .constants import VALID_CURRENCIES
|
||||
|
||||
|
||||
def validate_currency(value: str):
|
||||
currency_values = [c[0] for c in VALID_CURRENCIES]
|
||||
if value not in currency_values:
|
||||
raise ValidationError("Invalid Currency")
|
||||
Reference in New Issue
Block a user