generate a 6 digit long unique code for each grouped order

This commit is contained in:
Laetitia Getti 2023-08-08 13:29:53 +02:00 committed by Laetitia Getti
parent 9e52ded095
commit 2083142d47
7 changed files with 91 additions and 2 deletions

View file

@ -0,0 +1,45 @@
# Generated by Django 4.2.1 on 2023-08-08 09:20
from django.db import migrations, models
import base36
import random
def create_code_from_pk(pk):
"""When a grouped order is created, we compute a unique code that will be used in url path
How we generate this code :
1. The instance pk, written in base36 (max 5 digits for now - we assume that there will not be more than 60466175 grouped orders)
2. A random int written in base36 (5 digits long)
3. Only the 6 first digits of this string
The use of pk in the beginning of the string guarantees the uniqueness, and the random part makes that we cannot guess the url path.
"""
base_36_pk = base36.dumps(pk)
random_string = base36.dumps(random.randint(1727605,60466175)) # generates a 5 digits long string
return f"{base_36_pk}{random_string}"
def set_code_default(apps, schema_editor):
"""Provides a default code to existing grouped orders during migration"""
GroupedOrder = apps.get_model("order","GroupedOrder")
for grouped_order in GroupedOrder.objects.all().iterator():
grouped_order.code = create_code_from_pk(grouped_order.pk)
grouped_order.save()
class Migration(migrations.Migration):
dependencies = [
("order", "0024_alter_item_options"),
]
operations = [
migrations.AddField(
model_name="groupedorder",
name="code",
field=models.CharField(auto_created=True, null=True),
),
migrations.RunPython(set_code_default),
migrations.AlterField(
model_name="groupedorder",
name="code",
field=models.CharField(auto_created=True),
),
]

View file

@ -1,3 +1,6 @@
import random
import base36
from django.core.exceptions import ValidationError
from django.db import models
from django.urls import reverse
@ -5,6 +8,8 @@ from django.utils import timezone
from la_chariotte.settings import AUTH_USER_MODEL
CODE_LENGTH = 6
class GroupedOrder(models.Model):
name = models.CharField(
@ -20,6 +25,21 @@ class GroupedOrder(models.Model):
)
description = models.TextField("Description", null=True, blank=True)
total_price = models.DecimalField(max_digits=10, decimal_places=2, default=0)
code = models.CharField(auto_created=True)
def create_code_from_pk(self):
"""When a grouped order is created, we compute a unique code that will be used in url path
How we generate this code :
1. The instance pk, written in base36 (max 5 digits for now - we assume that there will not be more than 60466175 grouped orders)
2. A random int written in base36 (5 digits long)
3. Only the 6 first digits of this string
The use of pk in the beginning of the string guarantees the uniqueness, and the random part makes that we cannot guess the url path.
"""
base_36_pk = base36.dumps(self.pk)
random_string = base36.dumps(
random.randint(pow(36, CODE_LENGTH - 2), pow(36, CODE_LENGTH - 1) - 1)
) # generates a 5 digits long string
self.code = f"{base_36_pk}{random_string}"[:CODE_LENGTH]
def compute_total_price(self):
price = 0

View file

@ -1,10 +1,14 @@
import datetime
import pytest
from django.contrib import auth
from django.urls import reverse
from django.utils import timezone
from la_chariotte.order.models import GroupedOrder, Item
from la_chariotte.order.models import CODE_LENGTH, GroupedOrder, Item
from la_chariotte.order.tests.utils import create_grouped_order
pytestmark = pytest.mark.django_db
class TestGroupedOrderModel:
@ -124,6 +128,15 @@ class TestGroupedOrderModel:
"La date de livraison ne doit pas être dans le passé" in response.content.decode()
assert GroupedOrder.objects.count() == 0
def test_create_unique_code_from_pk(self, client_log):
grouped_order = create_grouped_order(
days_before_delivery_date=5,
days_before_deadline=2,
name="future",
orga_user=auth.get_user(client_log),
)
assert len(grouped_order.code) == CODE_LENGTH
class TestItemModel:
"""Tests for Item model"""

View file

@ -791,6 +791,7 @@ class TestGroupedOrderCreateView:
assert response.status_code == 302
assert response.url.endswith("gerer-produits")
assert models.GroupedOrder.objects.count() == 1
assert models.GroupedOrder.objects.first().code != ""
def test_create_grouped_order__anonymous_user(self, client):
create_gorder_url = reverse("order:create_grouped_order")

View file

@ -18,9 +18,11 @@ def create_grouped_order(
"""
date = timezone.now().date() + datetime.timedelta(days=days_before_delivery_date)
deadline = timezone.now() + datetime.timedelta(days=days_before_deadline)
return models.GroupedOrder.objects.create(
grouped_order = models.GroupedOrder.objects.create(
name=name, orga=orga_user, delivery_date=date, deadline=deadline
)
grouped_order.create_code_from_pk()
return grouped_order
def order_items_in_grouped_order(grouped_order):

View file

@ -122,6 +122,12 @@ class GroupedOrderCreateView(LoginRequiredMixin, generic.CreateView):
kwargs["user"] = self.request.user
return kwargs
def form_valid(self, form):
"""If the form is valid, generate a unique code and save"""
self.object = form.save()
self.object.create_code_from_pk()
return super().form_valid(form)
class GroupedOrderUpdateView(UserPassesTestMixin, generic.UpdateView):
"""View for updating a grouped order"""

View file

@ -13,6 +13,8 @@ asn1crypto==1.5.1
# oscrypto
# pyhanko
# pyhanko-certvalidator
base36==0.1.1
# à la main
certifi==2023.5.7
# via
# requests