Django best practice models question (from a beginner) - django

I want to create a model in Django that can describes a garden with rows for plants. Every plant needs some room to grow and i want to describe how many plants would fit in a row - I am a little bit stuck on how to best describe this in Django:
from django.db import models
from django.contrib.auth.models import User
class Plant(models.Model):
name = models.CharField('Plantname', max_length=120)
size = models.PositiveIntegerField("area around plant in cm")
qty = models.PositiveIntegerField("number of plants")
color = models.CharField("color of blossom", max_length=20)
class Patch(models.Model):
FERT = "fertilized"
BIO = "biological"
METHODS = ((FERT, "Fertilized"), (BIO, "Biological"))
type = models.Charfield("kind of patch", max_length=20, choices=METHODS)
qty = models.PositiveIntegerField("number of patches")
size = models.PositiveIntegerField("length of patch in cm")
class task(models.Model):
task_id = models.PositiveIntegerField('Task ID')
person = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL)
patches = models.ManyToManyField(Patch)
plants = models.ManyToManyField(Plant)
Now I create:
two example patches with 300 and 400cm length
two example plants with 20 and 15cm length
and I want to assign a task to a person that has the number of patches and plants he needs to work on.
So when a task is created, I need to:
ask for the size and number of the patches
Choose the type of plant
Calculate max. amount of plants possible
ask for the number of plants and set it max(plants) if number > max(plants)
create a task with the total quantity of patches and plants
can someone please help me out :)

Your models look fine. You'll have to ask for user input using Django Forms. You can probably use a ModelForm for your Task model, then override the save()
method to add your business logic.
Also, you should rename class task to class Task (or better still class FarmingTask since 'task' is a keyword used in several software libraries/languages and might conflict at a later time.)

Related

Populating django model with objects from other model

I'm new to django, but working on an app for a volunteer sailing organization in my local area. Not sure how to ask this question since it's fairly general but I want the following to happen based on two models;
Yacht class (boat name, skipper, color, etc.)
Race_Event class (event date, time results for each boat)
Step 1: The user will need to create a Race_Event each week. I want the boats from the Yacht model to be loaded into the Race_Event.
Step 2: The user will enter race times for each boat.
Is there a way to pre-load objects from one model into another? With a ForeignKey the user has to add the boats each time. Any direction for me to research would be helpful.
Here is the simplified code so far;
class Yacht (models.Model):
yacht_classes = [('A', 'A'),('A1', 'A1'),]
yacht_type = [('J-29','J-29'),('J-24','J-24'),]
yacht_name = models.CharField(max_length=75)
yacht_type = models.CharField(max_length=25, choices=yacht_type,
default='J-29')
yacht_class = models.CharField(max_length=25, choices=yacht_classes)
skipper = models.ForeignKey(Skipper, on_delete=models.CASCADE)
def __str__(self):
return self.yacht_name
class Event (models.Model):
race_date = models.DateTimeField(default=timezone.now)
#yachts = #how to Include Yacht.objects.all() to the field?
class Results (models.Model):
pass
Thanks
Yes, u can use signals...
after objects is saved u can call post_save and add all yachts to race
more => https://docs.djangoproject.com/en/3.1/ref/signals/#post-save
but i dont think this is good way...
(not every time all the data must be present or must be saved => this save rows in database)
i recomment you to use m2M between race and ship with throught table where time is saved in table between.
then its on you how you present this problem to end-user.
with this solution you save only data which are needed.
this can be done with
https://docs.djangoproject.com/en/3.1/topics/db/models/#extra-fields-on-many-to-many-relationships

Proper way to annotate a rank field for a queryset

Assume models like this:
class Person(models.Model):
name = models.CharField(max_length=20)
class Session(models.Model):
start_time = models.TimeField(auto_now_add=True)
end_time = models.TimeField(blank=True, null=True)
person = models.ForeignKey(Person)
class GameSession(models.Model):
game_type = models.CharField(max_length=2)
score = models.PositiveIntegerField(default=0, blank=True)
session = models.ForeignKey(Session)
I want to have a queryset function to return total score of each person which is addition of all his games score and all times he has spent in all his sessions alongside with a rank that a person has relative to all persons. Something like below:
class DenseRank(Func):
function = 'DENSE_RANK'
template = '%(function)s() Over(Order by %(expressions)s desc)'
class PersonQuerySet(models.query.QuerySet):
def total_scores(self):
return self.annotate(total_score=some_fcn_for_calculate).annotate(rank=DenseRank('total_score'))
I could find a way to calculate total score, but dense rank is not what I want, because it just calculates rank based on persons in current queryset but I want to calculate rank of a person relative to all persons.
I use django 1.11 and postgres 10.5, please suggest me a proper way to find rank of each person in a queryset because I want to able to add another filter before or after calculating total_score and rank.
Sadly, it is not a possible operation since (to me) the postgresql WHERE operation (filter/exclude) narrows the rows before the aggregation functions can work on them.
The only solution I found is to simply compute the ranking for all Person with a separate queryset and then, to annotate your queryset with these results.
This answer (see the improved method) explains how to "annotate a queryset with externally prepared data in a dict".
Here is the implementation I made for your models:
class PersonQuerySet(models.QuerySet):
def total_scores(self):
# compute the global ranking
ranks = (Person.objects
.annotate(total_score=models.Sum('session__gamesession__score'))
.annotate(rank=models.Window(expression=DenseRank(),
order_by=models.F('total_score').decs()))
.values('pk', 'rank'))
# extract and put ranks in a dict
rank_dict = dict((e['pk'], e['rank']) for e in ranks)
# create `WHEN` conditions for mapping filtered Persons to their Rank
whens = [models.When(pk=pk, then=rank) for pk, rank in rank_dict.items()]
# build the query
return (self.annotate(rank=models.Case(*whens, default=0,
output_field=models.IntegerField()))
.annotate(total_score=models.Sum('session__gamesession__score')))
I tested it with Django 2.1.3 and Postgresql 10.5, so the code may lightly change for you.
Feel free to share a version compatible with Django 1.11!

Multiple payments related to multiple customer implementation in django

I'm writing a customer management system for my business and got stuck on the payments entry system. This will run on a local dedicated server and should have only one user so code performance is not really an issue.
Every adult customer who enters the store is given a numbered card (Card, for the rest of this question) and his/her ID ( from Customer model ) is attached to it by a foreign key relation. There is an "entrance fee subtotal", which is the result of a choice field on Card model (there's only two choices and those won't change for a long time) plus kids 'fees'.
This, along with other two kind of models ( Product and Service), will compose the customer's bill. I have it working just fine, except on the payments registration.
As many Customers may be part of a family, and they may split their total bill quite often, I do believe Payment should be a model with an ManyToManyField related to Card so it could cover multiple payments methods ( treated as another choice field, since it will be either money, credit or debit cards ) but I can't figure it out how to model it neither how to handle it in my view/template.
I'm using django 1.9 & postgres 9.5 & python 2.7.
Bootstrap 3 along with some JS for styling (probably irrelevant).
Enough said, here's some code:
models.py
class Customer(models.Model):
id = models.AutoField(primary_key=True) #unnecessary but I had already written it
name = models.CharField(max_length=40)
last = models.CharField(max_length=80)
class Card(models.Model):
entrance_type1 = 1
entrance_type2 = 2
entrance_choices = (
(entrance_type1, 'Fun'),
(entrance_type2, 'Really Fun, kinda expensive'),
)
entrance_types = {
1:"Fun",
2:"Really Fun",
}
entrance_fee= {
'kid':5.0,
entrance_type1:15.0,
entrance_type1:35.0,
}
id = models.AutoField(primary_key=True) #Yeah, I do that
date = models.DateTimeField(auto_now=True, auto_now_add=False)
card_number = models.IntegerField()
entrance_type = models.PositiveIntegerField(choices=entrance_choices)
kids_number = models.PositiveIntegerField()
id_costumer = models.ForeignKey(Customer)
entrances_value = models.DecimalField(max_digits=6, decimal_places=2)
#will be entrance_fee[entrance_type] + entrance_fee['kid'] * kids_number
status = models.BooleanField(default=1) #should be 0 after payment(s)
Anyway, I really need help modelling payments for those. It should contain payment method, date and to which Cards it's related to.
I'm already getting ideas on the views/template step so I won't be strict about those on answers.
I do believe my question is kinda fuzzy and confuse, but can't figure how to make it better ( and maybe this is why I can't solve it by myself ) so please comment in your doubts and I'll edit it ( including removing this part when it does improve) after lunch.
Thanks in advance

Django - Displaying result information while optimizing database queries with models that multiple foreign key relationships

So I'm trying to put together a webpage and I am currently have trouble putting together a results page for each user in the web application I am putting together.
Here are what my models look like:
class Fault(models.Model):
name = models.CharField(max_length=255)
severity = models.PositiveSmallIntegerField(default=0)
description = models.CharField(max_length=1024, null=False, blank=False)
recommendation = models.CharField(max_length=1024, null=False, blank=False)
date_added = models.DateTimeField(_('date added'), default=timezone.now)
...
class FaultInstance(models.Model):
auto = models.ForeignKey(Auto)
fault = models.ForeignKey(Fault)
date_added = models.DateTimeField(_('date added'), default=timezone.now)
objects = FaultInstanceManager()
...
class Auto(models.Model):
label = models.CharField(max_length=255)
model = models.CharField(max_length=255)
make = models.CharField(max_length=255)
year = models.IntegerField(max_length=4)
user = models.ForeignKey(AUTH_USER_MODEL)
...
I don't know if my model relationships are ideal, however it made sense it my head. So each user can have multiple Auto objects associated to them. And each Auto can have multiple FaultInstance objects associated to it.
In the results page, I want to list out the all the FaultInstances that a user has across their Autos. And under each listed FaultInstance I will have a list of all the autos that the user owns that has the fault, with its information (here is kind of what I had in mind).
All FaultInstance Listing Ordered by Severity (large number to low number)
FaultInstance:
FaultDescription:
FaultRecommendation:
ListofAutosWithFault:
AutoLabel AutoModel AutoYear ...
AutoLabel AutoModel AutoYear ...
Obviously, do things the correct way would mean that I want to do as much of the list creation in the Python/Django side of things and avoid doing any logic or processing in the template. I am able to create a list per severity with the a model manager as seen here:
class FaultInstanceManager(models.Manager):
def get_faults_by_user_severity(self, user, severity):
faults = defaultdict(list)
qs_faultinst = self.model.objects.select_related().filter(
auto__user=user, fault__severity=severity
).order_by('auto__make')
for result in qs_faultinst:
faults[result.fault].append(result)
faults.default_factory = None
return faults
I still need to specify each severity but I guess if I only have 5 severity levels, I can create a list for each severity level and pass each individual one to template. Any suggestions for this is appreciated. However, thats not my problem. My stopping point right now is that I want to create a summary table at the top of their report which can give the user breakdown of fault instances per make|model|year. I can't think of the proper query or data structure to pass on to the template.
Summary (table of all the FaultInstances with the following column headers):
FaultInstance Make|Model|Year NumberOfAutosAffected
This will let me know metrics for a make or a model or a year (in the example below, its separating faults based on model). I'm listing FaultInstances because I'm only listed Faults that a connected to a user.
For Example
Bad Starter Nissan 1
Bad Tailight Honda 2
Bad Tailight Nissan 1
And I am such a perfectionist that I want to do this while optimizing database queries. If I can create a data structure in my original query that will be easily parsed in template and still get both these sections in my report (maybe a defaultdict of a defaultdict(list)), thats what I want to do. Thanks for the help and hopefully my question is thorough and makes sense.
It makes sense to use related names because it simplifies your query. Like this:
class FaultInstance(models.Model):
auto = models.ForeignKey(Auto, related_name='fault_instances')
fault = models.ForeignKey(Fault, related_name='fault_instances')
...
class Auto(models.Model):
user = models.ForeignKey(AUTH_USER_MODEL, related_name='autos')
In this case you can use:
qs_faultinst = user.fault_instances.filter(fault__severity=severity).order_by('auto__make')
instead of:
qs_faultinst = self.model.objects.select_related().filter(
auto__user=user, fault__severity=severity
).order_by('auto__make')
I can't figure out your summary table, may be you meant:
Fault Make|Model|Year NumberOfAutosAffected
In this case you can use aggregation. But It (grouping) would still be slow if you have enough data. The one easy solution is just to denormalize data by creating extra model and create few signals to update it or you can use cache.
If you have a predefined set of severities then think about this:
class Fault(models.Model):
SEVERITY_LOW = 0
SEVERITY_MIDDLE = 1
SEVERITY_HIGH = 2
...
SEVERITY_CHOICES = (
(SEVERITY_LOW, 'Low'),
(SEVERITY_MIDDLE, 'Middle'),
(SEVERITY_HIGH, 'High'),
...
)
...
severity = models.PositiveSmallIntegerField(default=SEVERITY_LOW,
choices=SEVERITY_CHOICES)
...
In your templates you can just iterate through Fault.SEVERITY_CHOICES.
Update:
Change your models:
Аllocate model into a separate model:
class AutoModel(models.Model):
name = models.CharField(max_length=255)
Change the field model of model Auto :
class Auto(models.Model):
...
auto_model = models.ForeignKey(AutoModel, related_name='cars')
...
Add a model:
class MyDenormalizedModelForReport(models.Model):
fault = models.ForeignKey(Fault, related_name='reports')
auto_model = models.ForeignKey(AutoModel, related_name='reports')
year = models.IntegerField(max_length=4)
number_of_auto_affected = models.IntegerField(default=0)
Add a signal:
def update_denormalized_model(sender, instance, created, **kwargs):
if created:
rep, dummy_created = MyDenormalizedModelForReport.objects.get_or_create(fault=instance.fault, auto_model=instance.auto.auto_model, year=instance.auto.year)
rep.number_of_auto_affected += 1
rep.save()
post_save.connect(update_denormalized_model, sender=FaultInstance)

Embed product-variance logic into Django models

I wonder how I would model my Products model to auto-create (and that the admin-App would also understand it) variants of a Product based on it's variant-parts.
My Products have;
Colors
Sizes
and can probably get more features in the future.
How would I model my Product class to generate all variants of the Product?
Say I would create a new Product in Colors Red Blue Green and in Sizes XS S M L XL.
class Product(models.Model):
name = models.CharField(max_length=200)
class Color(models.Model):
product = models.ForeignKey(Product)
name = models.CharField(max_length=200)
class Size(models.Model):
product = models.ForeignKey(Product)
name = models.CharField(max_length=200)
class FutureVariant(models.Model):
product = models.ForeignKey(Product)
name = models.CharField(max_length=200)
# etc.
Now when I would need a smart method that when I would auto-create all color-size-[FUTURE VARIANT] for that product.
So I would tell Django;
Create new Product
In the colors Red Blue Green
In the sizes XS S M L XL
And the Product class would go and produce Products with all possible combinations in the products_product table.
I'm almost sure that this has design flaws. But I'm just curious how to put this logic in the ORM, and not to write weird procedural code, which would probably go against the DRY principal.
In Database logic I would think of something like this;
PRODUCTS
- id
- name
PRODUCTS_VARIANTS_COLORS
- id
- name
- html_code
PRODUCTS_VARIANTS_SIZES
- id
- name
PRODUCTS_VARIANTS_TABLES
- table_name
- table_id
PRODUCTS_VARIANTS
- product_id
- variant_table
- variant_id
This way I could make endless variant tables, as long as I would register them in my PRODUCTS_VARIANTS_TABLES and store their name as relevant. PRODUCTS_VARIANTS would hold all the the variants of the product, including combinations of them all. I am also aiming to have a selection-phase where the user can chose (in a HTML checkbox-list) which variants it does and doesn't want.
The problem (I think) is that this would not really comply with a logic in the ORM.
I don't know if you are asking about alternatives or just looking to make your way work, but what about splitting a product from it's attributes?
So instead of having separate models for attributes, you just have an Attribute model. This way you are future-proofing your database so you can easily add more attributes (like if you have products with a height and width instead of just color or size).
class AttributeBase(models.Model):
label = models.CharField(max_length=255) # e.g. color, size, shape, etc.
...
class Attribute(models.Model):
base = models.ForeignKey('AttributeBase', related_name='attributes')
value = models.CharField(max_length=255) # e.g. red, L, round, etc.
internal_value = models.CharField(max_length=255, null=True, blank=True) # other values you may need e.g. #ff0000, etc.
...
class ProductAttribute(Attribute):
product = models.ForeignKey('Product', related_name='attributes')
It now becomes very easy to create all attributes for a product...
class Product(models.Model):
...
def add_all_attributes(self):
for attribute in Attribute.objects.all():
self.attributes.add(attribute)
now when you use product.add_all_attributes() that product will contain every attribute. AND you can even make it add attributes of a certain AttributeBase
def add_all_attributes_for_base(self, label):
base = AttributeBase.objects.get(label=label)
for attribute in base.attributes.all():
self.attributes.add(attribute)
You could write something as:
class Product(models.Model):
#classmethod
def create_variants(cls):
# compute all possible combinations
combinations = ...
for combination in combinations:
Product.objects.create(**combination)
Creating all the combinations would indeed happen through registering the possible variants and their possible values.
Note that ORM is there to help you map Django objects to database records, it doesn't help you with producing the database records (read: Django models) that you wish to save.