forked from uncloud/uncloud
prevent slug = product for product and resource
This commit is contained in:
parent
f881908b74
commit
9dc207ea4c
8 changed files with 117 additions and 5 deletions
44
uncloud_v3/README-2025.org
Normal file
44
uncloud_v3/README-2025.org
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
* Generally to be done for prod [44%]
|
||||
** TODO Add description to product
|
||||
- Maybe markdown later
|
||||
** TODO Link orders to users
|
||||
** TODO Maybe i18n on everything
|
||||
** PROGRESS Re-understand get_context_data
|
||||
- gets additional context such as other models / related models
|
||||
- Unsure when the context data is being used.
|
||||
- Usually used in the Detailview of the thing
|
||||
- so likely referenced by a template or View
|
||||
** TODO Prevent a product or resource to be named "product"
|
||||
Because of initial['product'] = self.kwargs['product']
|
||||
|
||||
|
||||
|
||||
** DONE Add default values to resources
|
||||
CLOSED: [2025-03-08 Sat 12:27]
|
||||
** DONE Re-understand get_form_kwargs
|
||||
CLOSED: [2025-03-08 Sat 11:48]
|
||||
- Build the keyword arguments required to instantiate the form.
|
||||
- Kinda defining the keys needed
|
||||
|
||||
https://docs.djangoproject.com/en/5.1/ref/class-based-views/mixins-editing/
|
||||
** DONE Re-understand get_initial
|
||||
CLOSED: [2025-03-08 Sat 11:48]
|
||||
- Kinda getting the (initial) values for the keys
|
||||
- populates form values
|
||||
" Retrieve initial data for the form. By default, returns a
|
||||
copy of initial."
|
||||
https://docs.djangoproject.com/en/5.1/ref/class-based-views/mixins-editing/
|
||||
|
||||
** DONE Why are (one time) prices separate from resources?
|
||||
CLOSED: [2025-03-08 Sat 11:31]
|
||||
A: because duration prices need to be separate.
|
||||
|
||||
- onetime price seems to be reasonable to be inside resource
|
||||
- Price per timeframe must be separate
|
||||
- Thus onetime price was also added separately
|
||||
* Bills [0%]
|
||||
** TODO Show bills
|
||||
** TODO Allow to create / send bills
|
||||
* OIDC integration [%]
|
||||
** Write code
|
||||
** Test with authentik
|
||||
|
|
@ -64,6 +64,7 @@ machine. Use `kubectl get nodes` to verify minikube is up and running.
|
|||
* resources should have a slug
|
||||
* can be used as an identifier and non unique names
|
||||
* Execute collectstatic for docker
|
||||
* OIDC / use authentik
|
||||
|
||||
#### 3.1 (validation release, planned)
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ class ProductOneTimeOrderForm(forms.Form):
|
|||
def __init__(self, resources, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
for res in resources:
|
||||
print(res)
|
||||
field_name = f"{res.slug}"
|
||||
self.fields[field_name] = forms.FloatField(
|
||||
required=True,
|
||||
|
|
|
|||
18
uncloud_v3/app/migrations/0006_resource_default_value.py
Normal file
18
uncloud_v3/app/migrations/0006_resource_default_value.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 5.0.2 on 2025-03-08 02:42
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('app', '0005_alter_productorder_timeframe_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='resource',
|
||||
name='default_value',
|
||||
field=models.FloatField(blank=True, null=True),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
# Generated by Django 5.0.2 on 2025-03-08 03:31
|
||||
|
||||
import app.models
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('app', '0006_resource_default_value'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='product',
|
||||
name='slug',
|
||||
field=models.SlugField(null=True, unique=True, validators=[app.models.validate_name_not_product]),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='resource',
|
||||
name='slug',
|
||||
field=models.SlugField(null=True, unique=True, validators=[app.models.validate_name_not_product]),
|
||||
),
|
||||
]
|
||||
|
|
@ -3,6 +3,20 @@ from django.contrib.auth import get_user_model
|
|||
from django.utils import timezone
|
||||
from django.urls import reverse
|
||||
from django.db.models import Q
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
def validate_name_not_product(value):
|
||||
"""
|
||||
We want to prevent overriding our own code.
|
||||
So the hardcoded name "product" may not be used as a product or resource name
|
||||
"""
|
||||
|
||||
if value == "product":
|
||||
raise ValidationError(
|
||||
_("%(value)s is not allowed as the name"),
|
||||
params={"value": value},
|
||||
)
|
||||
|
||||
class Currency(models.Model):
|
||||
slug = models.SlugField(null=True, unique=True)
|
||||
|
|
@ -56,11 +70,12 @@ class PricePerTime(models.Model):
|
|||
return f"{self.value}{self.currency.short_name}/{self.timeframe}"
|
||||
|
||||
class Resource(models.Model):
|
||||
slug = models.SlugField(null=True, unique=True) # primary identifier
|
||||
slug = models.SlugField(null=True, unique=True, validators=[validate_name_not_product]) # primary identifier
|
||||
name = models.CharField(max_length=128, unique=False) # CPU, RAM
|
||||
unit = models.CharField(max_length=128) # Count, GB
|
||||
minimum_units = models.FloatField(null=True, blank=True) # might have min
|
||||
maximum_units = models.FloatField(null=True, blank=True) # might have max
|
||||
default_value = models.FloatField(null=True, blank=True) # default value to show
|
||||
step_size = models.FloatField(default=1) # step size
|
||||
|
||||
price_per_time = models.ManyToManyField(PricePerTime, blank=True)
|
||||
|
|
@ -68,6 +83,7 @@ class Resource(models.Model):
|
|||
null=True, blank=True,
|
||||
on_delete=models.CASCADE)
|
||||
|
||||
|
||||
def __str__(self):
|
||||
if self.minimum_units:
|
||||
minimum = self.minimum_units
|
||||
|
|
@ -89,7 +105,7 @@ class Product(models.Model):
|
|||
Describes a product a user can buy
|
||||
"""
|
||||
|
||||
slug = models.SlugField(null=True, unique=True)
|
||||
slug = models.SlugField(null=True, unique=True, validators=[validate_name_not_product])
|
||||
name = models.CharField(max_length=128, unique=True)
|
||||
|
||||
resources = models.ManyToManyField(Resource, blank=True) # List of REQUIRED resources
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<h2>Order {{ product }}</h2>
|
||||
|
||||
{% if timeframe %}
|
||||
<p>Timeframe: {{ timeframe }}</p>
|
||||
<p>Selected timeframe: {{ timeframe }}</p>
|
||||
{% endif %}
|
||||
|
||||
<form method="post" >
|
||||
|
|
|
|||
|
|
@ -29,11 +29,16 @@ class ProductOneTimeOrderView(FormView):
|
|||
return reverse("order-confirmation")
|
||||
|
||||
def get_form_kwargs(self):
|
||||
"""
|
||||
Keys for the form
|
||||
"""
|
||||
kwargs = super().get_form_kwargs()
|
||||
|
||||
# Set the product so the form can retrieve the resources
|
||||
product = get_object_or_404(Product, slug=self.kwargs['product'])
|
||||
kwargs['resources'] = product.resources.all()
|
||||
|
||||
print(f"kwargs = {kwargs}")
|
||||
return kwargs
|
||||
|
||||
def get_initial(self):
|
||||
|
|
@ -41,10 +46,15 @@ class ProductOneTimeOrderView(FormView):
|
|||
Initial values for the form
|
||||
"""
|
||||
|
||||
initial = super().get_initial()
|
||||
|
||||
initial = super().get_initial()
|
||||
initial['product'] = self.kwargs['product']
|
||||
|
||||
product = get_object_or_404(Product, slug=self.kwargs['product'])
|
||||
for res in product.resources.all():
|
||||
if res.default_value:
|
||||
initial[res.slug] = res.default_value
|
||||
|
||||
if 'timeframe' in self.kwargs:
|
||||
initial['timeframe'] = self.kwargs['timeframe']
|
||||
return initial
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue