mirror of
https://github.com/Crocmagnon/checkout.git
synced 2024-12-22 14:11:48 +01:00
Implement data model
This commit is contained in:
parent
1288a30308
commit
eead96359b
13 changed files with 353 additions and 1 deletions
54
poetry.lock
generated
54
poetry.lock
generated
|
@ -333,6 +333,18 @@ python-versions = ">=3.6"
|
|||
[package.dependencies]
|
||||
pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
|
||||
|
||||
[[package]]
|
||||
name = "pillow"
|
||||
version = "9.1.0"
|
||||
description = "Python Imaging Library (Fork)"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.extras]
|
||||
docs = ["olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-issues (>=3.0.1)", "sphinx-removed-in", "sphinx-rtd-theme (>=1.0)", "sphinxext-opengraph"]
|
||||
tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"]
|
||||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "2.5.2"
|
||||
|
@ -630,7 +642,7 @@ brotli = ["brotli"]
|
|||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.10"
|
||||
content-hash = "e141bb1b1bfaaea951e57b266d4cc4619e598f853d27f40964405303000e7ea8"
|
||||
content-hash = "86cec3b5cbae86ec8a2e77f3e77139e12c48bcc8aa79918daed41f56f48597f4"
|
||||
|
||||
[metadata.files]
|
||||
asgiref = [
|
||||
|
@ -932,6 +944,46 @@ packaging = [
|
|||
{file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
|
||||
{file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
|
||||
]
|
||||
pillow = [
|
||||
{file = "Pillow-9.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:af79d3fde1fc2e33561166d62e3b63f0cc3e47b5a3a2e5fea40d4917754734ea"},
|
||||
{file = "Pillow-9.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:55dd1cf09a1fd7c7b78425967aacae9b0d70125f7d3ab973fadc7b5abc3de652"},
|
||||
{file = "Pillow-9.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:66822d01e82506a19407d1afc104c3fcea3b81d5eb11485e593ad6b8492f995a"},
|
||||
{file = "Pillow-9.1.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5eaf3b42df2bcda61c53a742ee2c6e63f777d0e085bbc6b2ab7ed57deb13db7"},
|
||||
{file = "Pillow-9.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01ce45deec9df310cbbee11104bae1a2a43308dd9c317f99235b6d3080ddd66e"},
|
||||
{file = "Pillow-9.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:aea7ce61328e15943d7b9eaca87e81f7c62ff90f669116f857262e9da4057ba3"},
|
||||
{file = "Pillow-9.1.0-cp310-cp310-win32.whl", hash = "sha256:7a053bd4d65a3294b153bdd7724dce864a1d548416a5ef61f6d03bf149205160"},
|
||||
{file = "Pillow-9.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:97bda660702a856c2c9e12ec26fc6d187631ddfd896ff685814ab21ef0597033"},
|
||||
{file = "Pillow-9.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:21dee8466b42912335151d24c1665fcf44dc2ee47e021d233a40c3ca5adae59c"},
|
||||
{file = "Pillow-9.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b6d4050b208c8ff886fd3db6690bf04f9a48749d78b41b7a5bf24c236ab0165"},
|
||||
{file = "Pillow-9.1.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5cfca31ab4c13552a0f354c87fbd7f162a4fafd25e6b521bba93a57fe6a3700a"},
|
||||
{file = "Pillow-9.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed742214068efa95e9844c2d9129e209ed63f61baa4d54dbf4cf8b5e2d30ccf2"},
|
||||
{file = "Pillow-9.1.0-cp37-cp37m-win32.whl", hash = "sha256:c9efef876c21788366ea1f50ecb39d5d6f65febe25ad1d4c0b8dff98843ac244"},
|
||||
{file = "Pillow-9.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:de344bcf6e2463bb25179d74d6e7989e375f906bcec8cb86edb8b12acbc7dfef"},
|
||||
{file = "Pillow-9.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:17869489de2fce6c36690a0c721bd3db176194af5f39249c1ac56d0bb0fcc512"},
|
||||
{file = "Pillow-9.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:25023a6209a4d7c42154073144608c9a71d3512b648a2f5d4465182cb93d3477"},
|
||||
{file = "Pillow-9.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8782189c796eff29dbb37dd87afa4ad4d40fc90b2742704f94812851b725964b"},
|
||||
{file = "Pillow-9.1.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:463acf531f5d0925ca55904fa668bb3461c3ef6bc779e1d6d8a488092bdee378"},
|
||||
{file = "Pillow-9.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f42364485bfdab19c1373b5cd62f7c5ab7cc052e19644862ec8f15bb8af289e"},
|
||||
{file = "Pillow-9.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3fddcdb619ba04491e8f771636583a7cc5a5051cd193ff1aa1ee8616d2a692c5"},
|
||||
{file = "Pillow-9.1.0-cp38-cp38-win32.whl", hash = "sha256:4fe29a070de394e449fd88ebe1624d1e2d7ddeed4c12e0b31624561b58948d9a"},
|
||||
{file = "Pillow-9.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:c24f718f9dd73bb2b31a6201e6db5ea4a61fdd1d1c200f43ee585fc6dcd21b34"},
|
||||
{file = "Pillow-9.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fb89397013cf302f282f0fc998bb7abf11d49dcff72c8ecb320f76ea6e2c5717"},
|
||||
{file = "Pillow-9.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c870193cce4b76713a2b29be5d8327c8ccbe0d4a49bc22968aa1e680930f5581"},
|
||||
{file = "Pillow-9.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69e5ddc609230d4408277af135c5b5c8fe7a54b2bdb8ad7c5100b86b3aab04c6"},
|
||||
{file = "Pillow-9.1.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:35be4a9f65441d9982240e6966c1eaa1c654c4e5e931eaf580130409e31804d4"},
|
||||
{file = "Pillow-9.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82283af99c1c3a5ba1da44c67296d5aad19f11c535b551a5ae55328a317ce331"},
|
||||
{file = "Pillow-9.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a325ac71914c5c043fa50441b36606e64a10cd262de12f7a179620f579752ff8"},
|
||||
{file = "Pillow-9.1.0-cp39-cp39-win32.whl", hash = "sha256:a598d8830f6ef5501002ae85c7dbfcd9c27cc4efc02a1989369303ba85573e58"},
|
||||
{file = "Pillow-9.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:0c51cb9edac8a5abd069fd0758ac0a8bfe52c261ee0e330f363548aca6893595"},
|
||||
{file = "Pillow-9.1.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a336a4f74baf67e26f3acc4d61c913e378e931817cd1e2ef4dfb79d3e051b481"},
|
||||
{file = "Pillow-9.1.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb1b89b11256b5b6cad5e7593f9061ac4624f7651f7a8eb4dfa37caa1dfaa4d0"},
|
||||
{file = "Pillow-9.1.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:255c9d69754a4c90b0ee484967fc8818c7ff8311c6dddcc43a4340e10cd1636a"},
|
||||
{file = "Pillow-9.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5a3ecc026ea0e14d0ad7cd990ea7f48bfcb3eb4271034657dc9d06933c6629a7"},
|
||||
{file = "Pillow-9.1.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5b0ff59785d93b3437c3703e3c64c178aabada51dea2a7f2c5eccf1bcf565a3"},
|
||||
{file = "Pillow-9.1.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7110ec1701b0bf8df569a7592a196c9d07c764a0a74f65471ea56816f10e2c8"},
|
||||
{file = "Pillow-9.1.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:8d79c6f468215d1a8415aa53d9868a6b40c4682165b8cb62a221b1baa47db458"},
|
||||
{file = "Pillow-9.1.0.tar.gz", hash = "sha256:f401ed2bbb155e1ade150ccc63db1a4f6c1909d3d378f7d1235a44e90d75fb97"},
|
||||
]
|
||||
platformdirs = [
|
||||
{file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"},
|
||||
{file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"},
|
||||
|
|
|
@ -16,6 +16,7 @@ requests = "^2.27.1"
|
|||
django-extensions = "^3.1.5"
|
||||
bpython = "^0.22.1"
|
||||
gunicorn = "^20.1.0"
|
||||
Pillow = "^9.1.0"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
pre-commit = "^2.7"
|
||||
|
|
|
@ -81,6 +81,7 @@ INSTALLED_APPS = [
|
|||
"anymail",
|
||||
"django_cleanup.apps.CleanupConfig",
|
||||
"common",
|
||||
"purchase",
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
|
|
0
src/purchase/__init__.py
Normal file
0
src/purchase/__init__.py
Normal file
42
src/purchase/admin.py
Normal file
42
src/purchase/admin.py
Normal file
|
@ -0,0 +1,42 @@
|
|||
from django.contrib import admin
|
||||
from django.contrib.admin import register
|
||||
|
||||
from purchase.models import Basket, BasketItem, PaymentMethod, Product
|
||||
|
||||
|
||||
@register(Product)
|
||||
class ProductAdmin(admin.ModelAdmin):
|
||||
list_display = ["name", "display_order", "unit_price"]
|
||||
search_fields = ["name"]
|
||||
|
||||
def unit_price(self, instance: Product):
|
||||
return instance.unit_price_cents / 100
|
||||
|
||||
|
||||
@register(PaymentMethod)
|
||||
class PaymentMethodAdmin(admin.ModelAdmin):
|
||||
list_display = ["name"]
|
||||
search_fields = ["name"]
|
||||
|
||||
|
||||
class BasketItemInline(admin.TabularInline):
|
||||
model = BasketItem
|
||||
fields = ["product", "quantity", "price"]
|
||||
extra = 0
|
||||
readonly_fields = ["price"]
|
||||
|
||||
def price(self, instance) -> float:
|
||||
return instance.price / 100
|
||||
|
||||
|
||||
@register(Basket)
|
||||
class BasketAdmin(admin.ModelAdmin):
|
||||
list_display = ["id", "status", "payment_method", "created_at", "price"]
|
||||
fields = ["created_at", "status", "payment_method"]
|
||||
list_filter = ["status", "payment_method"]
|
||||
date_hierarchy = "created_at"
|
||||
readonly_fields = ["created_at"]
|
||||
inlines = [BasketItemInline]
|
||||
|
||||
def price(self, instance) -> float:
|
||||
return instance.price / 100
|
6
src/purchase/apps.py
Normal file
6
src/purchase/apps.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class PurchaseConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "purchase"
|
131
src/purchase/migrations/0001_initial.py
Normal file
131
src/purchase/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,131 @@
|
|||
# Generated by Django 4.0.4 on 2022-04-24 14:14
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = []
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="Basket",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
("updated_at", models.DateTimeField(auto_now=True)),
|
||||
(
|
||||
"status",
|
||||
models.CharField(
|
||||
choices=[("DRAFT", "Draft"), ("COMPLETE", "Complete")],
|
||||
default="DRAFT",
|
||||
max_length=20,
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="PaymentMethod",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
("updated_at", models.DateTimeField(auto_now=True)),
|
||||
("name", models.CharField(max_length=50, unique=True)),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="Product",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
("updated_at", models.DateTimeField(auto_now=True)),
|
||||
("name", models.CharField(max_length=250, unique=True)),
|
||||
("image", models.ImageField(null=True, upload_to="")),
|
||||
("unit_price_cents", models.PositiveIntegerField()),
|
||||
("display_order", models.PositiveIntegerField()),
|
||||
],
|
||||
options={
|
||||
"ordering": ["display_order", "name"],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="BasketItem",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
("updated_at", models.DateTimeField(auto_now=True)),
|
||||
("quantity", models.PositiveIntegerField()),
|
||||
(
|
||||
"basket",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="items",
|
||||
to="purchase.basket",
|
||||
),
|
||||
),
|
||||
(
|
||||
"product",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="basket_items",
|
||||
to="purchase.product",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="basket",
|
||||
name="payment_method",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="baskets",
|
||||
to="purchase.paymentmethod",
|
||||
),
|
||||
),
|
||||
]
|
18
src/purchase/migrations/0002_alter_product_image.py
Normal file
18
src/purchase/migrations/0002_alter_product_image.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 4.0.4 on 2022-04-24 14:15
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("purchase", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="product",
|
||||
name="image",
|
||||
field=models.ImageField(blank=True, null=True, upload_to=""),
|
||||
),
|
||||
]
|
22
src/purchase/migrations/0003_alter_product_display_order.py
Normal file
22
src/purchase/migrations/0003_alter_product_display_order.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
# Generated by Django 4.0.4 on 2022-04-24 14:17
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
import purchase.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("purchase", "0002_alter_product_image"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="product",
|
||||
name="display_order",
|
||||
field=models.PositiveIntegerField(
|
||||
default=purchase.models.default_product_display_order
|
||||
),
|
||||
),
|
||||
]
|
0
src/purchase/migrations/__init__.py
Normal file
0
src/purchase/migrations/__init__.py
Normal file
73
src/purchase/models.py
Normal file
73
src/purchase/models.py
Normal file
|
@ -0,0 +1,73 @@
|
|||
from django.db import models
|
||||
|
||||
|
||||
class Model(models.Model):
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
||||
class PaymentMethod(Model):
|
||||
name = models.CharField(max_length=50, unique=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
def default_product_display_order():
|
||||
return Product.objects.last().display_order + 1
|
||||
|
||||
|
||||
class Product(Model):
|
||||
name = models.CharField(max_length=250, unique=True)
|
||||
image = models.ImageField(null=True, blank=True)
|
||||
unit_price_cents = models.PositiveIntegerField()
|
||||
display_order = models.PositiveIntegerField(default=default_product_display_order)
|
||||
|
||||
class Meta:
|
||||
ordering = ["display_order", "name"]
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Basket(Model):
|
||||
STATUS_DRAFT = "DRAFT"
|
||||
STATUS_COMPLETE = "COMPLETE"
|
||||
_STATUS_CHOICES = [
|
||||
(STATUS_DRAFT, "Draft"),
|
||||
(STATUS_COMPLETE, "Complete"),
|
||||
]
|
||||
payment_method = models.ForeignKey(
|
||||
to=PaymentMethod,
|
||||
on_delete=models.PROTECT,
|
||||
related_name="baskets",
|
||||
null=True,
|
||||
blank=True,
|
||||
)
|
||||
status = models.CharField(
|
||||
max_length=20, choices=_STATUS_CHOICES, default=STATUS_DRAFT
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return f"Panier #{self.id}"
|
||||
|
||||
@property
|
||||
def price(self) -> int:
|
||||
return sum(item.price for item in self.items.all())
|
||||
|
||||
|
||||
class BasketItem(Model):
|
||||
product = models.ForeignKey(
|
||||
to=Product, on_delete=models.PROTECT, related_name="basket_items"
|
||||
)
|
||||
basket = models.ForeignKey(
|
||||
to=Basket, on_delete=models.CASCADE, related_name="items"
|
||||
)
|
||||
quantity = models.PositiveIntegerField()
|
||||
|
||||
@property
|
||||
def price(self) -> int:
|
||||
return self.product.unit_price_cents * self.quantity
|
3
src/purchase/tests.py
Normal file
3
src/purchase/tests.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
3
src/purchase/views.py
Normal file
3
src/purchase/views.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
Loading…
Reference in a new issue