UpdateView that creates or increments existing instance - python

I have a model that keeps track how much items have been "bought" at a certain value.
For example, one could think of items as stocks, value as the price they have been bought at.
class Inventory(models.Model):
item = models.ForeignKey(Item, on_delete=models.CASCADE)
quantity = models.PositiveIntegerField()
value = models.PositiveIntegerField()
class Meta:
db_table = 'app_version'
constraints = [
models.UniqueConstraint(fields=['item', 'value'], name='unique valued items')
]
Now, I'm trying to write a class-based Create/UpdateView in which one simply declares which item has been bought at what price. Then, the view should either create a new instance or increment an existing instance's quantity.
I already figured out that an UpdateView might be the best start as I can overwrite the get_object method to have this "create or update" behaviour.
However, I'm not sure how to do the incrementing.
Perhaps I'm also completely on the wrong track and there is a much simpler solution I am missing here.
It would be great to get some ideas and input on how to approach this problem.
Thank you so much!

Just use get_or_update method. It allows you to know it's new instance or from db. But if you want to create an api, it's the better way to use django rest framework. It ships with a lot of build-in tools for quick api creation.

Related

Django model design pattern - get youngest related object

Django lets you follow relations link, but none of the filter methods let you get youngest/oldest, or max/min, afaik.
Laravel has "has one of many", and I wish that Django had something similar without requiring window functions, that have their own limitations.
Annotations despite being promising, are also a dead end.
Therefore I wonder what the best design is for the following situation:
I have "model A", whose instances will pass through several statuses during their lifecycle ("created", "processing", "complete"). I want to know what status an instance of model A currently has, but also have a record of when each status was in effect. I don't want to parse logs.
I thought a good approach was to create a status model (model B) whose foreign key is a model A instance. it becomes easy to see when each status was started and stopped.
However if I want to get the current status (an instance of model B) for all my model A instances, I need to do n+1 database queries. This seems sub-optimal.
What are some alternatives?
However if I want to get the current status (an instance of model B) for all my model A instances, I need to do n+1 database queries. This seems sub-optimal.
No, you can make use of Subquery expressions [Django-doc]. Indeed, if you have two models:
class Item(models.Model):
name = models.CharField(max_length=128)
class ItemStatus(models.Model):
item = models.ForeignKey(Item, on_delete=models.CASCADE)
status = models.CharField(max_length=128)
started = models.DateTimeField(auto_now_add=True)
You can annotate each Item with the last status with:
from django.db.models import OuterRef, Subquery
Item.objects.annotate(
last_status=Subquery(
ItemStatus.objects.filter(
item_id=OuterRef('pk')
).order_by('-started').values('status')[:1]
)
)
For each Item, there will be an extra attribute named .last_status that will contain the .status of the related ItemStatus that started last. If there is no such StatusItem, last_status will be None (NULL).
This will be determined by subqueries at the database side, hence it is done in the same query where you retrieve the Items, and thus does not suffer from the N+1 problem.

Django set model fields after create

I have a model Movie and a Model DefaultMoviePriceFor which saves the default prices for a specific genre:
class Movie(models.Model):
name = models.TextField('name', null=True)
genre = models.TextField('genre', null=True)
price = models.IntegerField('price', default=0)
class DefaultMoviePriceForGenre(models.Model):
genre = models.TextField('genre', null=True)
price = models.IntegerField('price', default=0)
Now I would like to fill up Movie.price with the default price of the Movie.genre everytime an object of Movie is instantiated.
Is there any good way to do so? I have SQL triggers in my head, they could do that, don't they? How is it done in django?
One way to do this is with the pre-save signal. This will be fired immediately before any instance of the model is saved, and will received a created boolean arg that will let you set the price only if the object is new.
There's also a post-save signal if for some reason you want to do it after saving, EG because your behavior depends on the new instance's PK. But pre-save should work here.
Alternatively, you can override the model class's save method. https://docs.djangoproject.com/en/2.1/topics/db/models/#overriding-model-methods
See this answer for some discussion of the pros and cons of the two approaches: Django signals vs. overriding save method
This is all assuming the desired behavior is exactly as described - look up the current default price of the genre when the instance is created and preserve it independently of future changes to the genre. If you want to do something more flexible - EG say "horror movies cost X now unless overridden, but if I change the default genre price later they should all update" you might be best served with a method on the Movie class that computes the price based on current state rather than setting it at creation and breaking that connection. But it depends what you want.

Implement items ordering in Django

Suppose, I want to build a simple TODO-app. I want to make it possible to create todo-items, and also it should be possible to rearrange items manually.
I made following model:
class Item(models.Model):
title = models.CharField(max_length=500)
Now, I need to add a special field, let's call it order, to keep custom ordering. It should be unique, and it should be greater for any new record, so I tried to make an AutoField.
class Item(models.Model):
title = models.CharField(max_length=500)
order = models.AutoField(primary_key=False)
But it turned out that Django doesn't support several auto fields for a single model.
I guess, it should be possible to write custom raw SQL code and use Postgres sequences directly, but it would look really ugly, and I don't want to write DB-specific code for such simple functionality.
So, here is a question: what is a common way to implement items ordering in Django? It sounds like a very common requirement, and, I think, it should be a simple way to do this.
As it turns out there is no straightforward way to implement this in Django. There are packages which help you, like this one
But I would recommend just look at their model implementation and fit your needs. models.py
You could use Item.objects.count() to automatically increment your field. Plug it in the save() method of your model so that your field is calculated each time you create an instance.

What is the best way to implement persistent data model in Django?

What I need is basically a database model with version control. So that every time a record is modified/deleted, the data isn't lost, and the change can be undone.
I've been trying to implement it myself with something like this:
from django.db import models
class AbstractPersistentModel(models.Model):
time_created = models.DateTimeField(auto_now_add=True)
time_changed = models.DateTimeField(null=True, default=None)
time_deleted = models.DateTimeField(null=True, default=None)
class Meta:
abstract = True
Then every model would inherit from AbstractPersistentModel.
Problem is, if I override save() and delete() to make sure they don't actually touch the existing data, I'll still be left with the original object, and not the new version.
After trying to come up with a clean, safe and easy-to-use solution for some hours, I gave up.
Is there some way to implement this functionality that isn't overwhelming?
It seems common enough problem that I thought it would be built into Django itself, or at least there'd be a well documented package for this, but I couldn't find any.
When I hear version control for models and Django, I immediately think of django-reversion.
Then, if you want to access the versions of an instance, and not the actual instance, simply use the Version model.
from reversion.models import Version
versions = Version.objects.get_for_object(instance)
I feel you can work around your issue not by modifying your models but by modifying the logic that access them.
So, you could have two models for your same object: one that can be your staging area, in which you store values as the ones you mention, such as time_created, time_modified, and modifying_user, or others. From there, in the code for your views you go through that table and select the records you want/need according to your design and store in your definitive table.

DJANGO: How to query multiple tables, equal model without looping

I'm looking for a nice way to query(DJANGO) temporal data, which is stored in different tables, but share the same model. Here's an example data model:
class myModel(models.Model):
x = models.FloatField()
class Meta:
db_table = "24-01-2017"
Now, I could of course loop over the different days, changing _meta.db_table for every iteration, but there should be a way to do this in a single query in DJANGO. This doesn't seem to be it, as DJANGO develops this may be outdated and I don't see anything related in the documentation.
How to handle this neatly?
Thanks in advance!
Edit 1
Hm, probably I'm just looking for a way to perform a outer join... But I can't find any implementation for this.

Categories

Resources