Query latest entry filter by criterion - django

I would like to output the latest entries, filtered by a specific criterion. I have this model :
class Task(models.Model):
active = models.BooleanField(default=True, verbose_name="Activation")
kind = models.CharField(max_length=1, default=2)
mode = models.CharField(max_length=32)
category = models.ForeignKey(TaskCategory, verbose_name="Catégorie")
city = models.ForeignKey(AppCity, verbose_name="Ville")
skill = models.ManyToManyField(SkillKind, null=True, blank=True, verbose_name="Compétence requise")
I want to get the fifth latest entry for each city. How can I achieve this?

You have no date/datetime field and no explicit ordering so I'm not sure what "latest" means in this context, but anyway: assuming the default ordering is ok for you or you set it in your queryset:
for city in AppCity.objects.all():
print city.task_set.reverse()[:5]

Related

Django REST: Dynamically add Model Fields

I'm working on a Django Rest project where I'm given two MySQL tables:
metrics: Contain a row for each potential metric
daily_data: Contains a row for each data entry where the column names refer to metrics from the 'metrics' table
What I want to do now, is creating new entries in 'metrics' which should be automatically added to existing 'daily_data' entries (with a default value) and displayed on the website.
Here is how the current models looks like:
class Metrics(model.Model):
metric_id = models.CharField(max_length=255, primary_key=True)
is_main_metric = models.BooleanField(default=False)
name = models.CharField(max_length=255, blank=False, null=False)
description = models.CharField(max_length=255, blank=False, null=False)
lower_bound = models.FloatField(default=0.0, null=False)
upper_bound = models.FloatField(default=0.0, null=False)
class Meta:
verbose_name_plural = "Metrics"
db_table = "metrics"
class DailyData(models.Model):
location = models.CharField(max_length=255, blank=False, null=False)
date = models.DateField(blank=False, null=False)
# then a static field for each metric is added that corresponds to a 'metric_id' in the table 'metrics':
metric_01 = models.FloatField(default=0.0, null=False)
metric_02 = models.FloatField(default=0.0, null=False)
metric_03 = models.FloatField(default=0.0, null=False)
...
class Meta:
verbose_name_plural = "Daily Data"
db_table = "daily_data"
Later on, the Javascript code iterates over all 'metrics' to display them with the corresponding values from a requested 'daily_data' entry. Here is a small example:
let resp = await axios.get(`${API_URL}/daily_data/?location=berlin&date=2021-01-07`);
let data = resp.data[0];
METRICS.forEach(metric => {
let name = metric.name;
let description = metric.description;
let value = data[metric.metric_id];
$content.append(
` <div class="row">
<span>${name}:</span>
<span>${value}</span>
<span>${description}"</span>
</div> `
);
...
}
For the case that all metrics are pre-defined, the application is running fine. If I want to add a new metric, I create a new row in the database table 'metrics', then add the field manually to the 'DailyData' model from above, and finally restart the server.
However, my problem now is that I need the possibility to add new metrics dynamically. I.e. if a user adds a new metric (for example with a POST request), the metric should be added as a column to all existing 'daily_data' entries and should be displayed as an additional field on the website.
The intention is basically something like this (I know that this won't work, but just to get the idea):
def onNewMetricCreation(newMetric):
metric_id = newMetric.metric_id
new_field = models.FloatField(default=0.0, null=False)
DailyData.appendField(metric_id, new_field)
Is there a way to achieve this and add these model fields dynamically? Or is my whole data structure faulty for this case?
Edit: To solve the problem I've actually changed my data structure a bit. I've added a MetricsData model that connects the DailyData with the Metrics and contains the corresponding values. This allows each DailyData object to have a different number of metrics and new ones can be added easily.
The new models look like this:
class DailyData(models.Model):
location = models.ForeignKey("Locations", on_delete=models.CASCADE, blank=False, null=False)
date = models.DateField(blank=False, null=False)
class MetricsData(models.Model):
data_entry = models.ForeignKey("DailyData", on_delete=models.CASCADE, related_name="data_entry")
metric = models.ForeignKey("Metrics", on_delete=models.CASCADE)
value = models.FloatField(default=0.0, null=False)
class Metrics(models.Model):
metric_id = models.CharField(max_length=255, primary_key=True)
...
If I understood you correct I belive you're looking for a ForeignKey(). You would add this to your model:
class DailyData(models.Model):
metrics = models.ForeignKey(Metrics, on_delete=models.CASCADE)
Go inside django admin and I think you'll understand how ForeignKeys work. It's a reference to the metrics instance. Ps. don't add this field dynamically, that's probably impossible. But with this you can simply add another row.
So if you reference an instance of metrics. And then change that. all daily_data that references that will be "changed" since they're still referenceing the same instance.
If you need to reference more the one metrics use ManyToMany
I strongly recommend that you add a Foreign Key for DailyData to Metrics model.
class Metrics(model.Model):
...
related_day = models.ForeignKey(DailyData, on_delete=models.CASCADE, related_name="metrics", related_query_name="metrics", null=True)
Now you also need to add a signal to trigger after creating a metric to connect that metric to its related data.
#receiver(post_save, sender=Metrics)
def add_to_daily_data(sender, instance, created, **kwargs):
if created:
# Put your logic to add a specific metric to a daily data
Also, this way you can access all metrics data related to specific DailyData objects hassle-free.
daily_data.metrics.all()

Django - Getting latest of each choice

I have two models: the gas station and the price of a product. The price can up to have 4 choices, one for each product type, not every station has all four products. I want to query the latest entry of each of those products, preferably in a single query:
class GasStation(models.Model):
place_id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=255, null=True, blank=True)
class Price(models.Model):
class Producto(models.TextChoices):
GASOLINA_REGULAR = 'GR', _('Gasolina regular')
GASOINA_PREMIUM = 'GP', _('Gasolina premium')
DIESEL_REGULAR = 'DR', _('Diesel regular')
DIESEL_PREMIUM = 'DP', _('Diesel premium')
product = models.CharField(max_length=2, choices=Producto.choices)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
price = models.FloatField(null=True, blank=True)
estacion = models.ForeignKey(GasStation,
on_delete=models.SET_NULL,
null=True,
related_name='prices')
I've tried with:
station.price.filter(product__in=['GR', 'GP', 'DR', 'DP']).latest()
But it only returns the latest of the whole queryset, not the latest price of each product type. I want to avoid querying for each individual product because some stations don't sell all types .Any advice?
You're looking for annotations and Subquery. Below is what I think might work. Your models aren't fully defined. If you need the whole Price instance, then this won't work for you. Subquery can only annotate a single field.
from django.db.models import OuterRef, Subquery
stations = GasStation.objects.annotate(
latest_regular=Subquery(
Price.objects.filter(station_id=OuterRef("pk"), product="GR").order_by('-updated').values("price")[:1]
),
latest_premium=Subquery(
Price.objects.filter(station_id=OuterRef("pk"), product="GP").order_by('-updated').values("price")[:1]
),
...
)
station = stations.get(something_here)
station.latest_premium, station.latest_regular
You can make this more concise by using a dict comprehension iterating over your Product short codes and then doing .annotate(**annotations)

Django Foreignkey with edit fields and select field how to put in form

I apologize if that question was raised before. But I have been struggling with this for weeks and couldn't find anything useful.
I have the following problem (it is simplified a lot but essentially my problem is presented)
I have a Model that has a lot of fields. It's called
class DocAide(models.Model):
id = models.AutoField(primary_key=True)
pulse = models.DecimalField('Pulse', max_digits=3, decimal_places=0)
weight = models.DecimalField('Weight (kg)', max_digits=3, decimal_places=0)
bp_sys = models.DecimalField('BP Sys', max_digits=3, decimal_places=0)
bp_dia = models.DecimalField('BP Dia', max_digits=3, decimal_places=0)
temperature = models.DecimalField('Temp. deg C', max_digits=2, decimal_places=1)
drugs = models.ManyToManyField(Drug, blank=True)
date = models.DateField(editable=False, default=timezone.now)
doctors_notes = models.TextField('Patient is complaining about:', default='')
note = models.TextField(max_length=100, default='')
The ForeignKey Drugs has Names of drugs with quantity I would like to have the possibility to select multiple drugs but with edit fields that show what dosage needs to be taken and when, it should be like a prescription. The Model looks like this:
class Drug(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=100, default='')
QUANTITY_STR = ['Bottle', 'Tablet' 'Injection', 'Capsules', 'other']
QUANTITY = ((str, str) for str in QUANTITY_STR)
quantity = models.CharField(max_length=2, choices=QUANTITY, default='Bottle')
category = models.CharField(max_length=150, default='')
strength = models.CharField(max_length=150, default='')
in_supply_stock = models.PositiveIntegerField(default=0)
in_main_stock = models.PositiveIntegerField(default=0)
date = models.DateField(editable=False, default=timezone.now)
charge = models.PositiveIntegerField(default=0)
morning = models.CharField(validators=[int_list_validator], max_length=3, default=0)
midday = models.CharField(validators=[int_list_validator], max_length=3, default=0)
evening = models.CharField(validators=[int_list_validator], max_length=3, default=0)
night = models.CharField(validators=[int_list_validator], max_length=3, default=0)
days = models.CharField(validators=[int_list_validator], max_length=3, default=0)
tablets = models.CharField(validators=[int_list_validator], max_length=3, default=0)
How can I accomplish that in a form or template. I tried with Inlineformset it doesn't work. Also later I would like to have them preselected as well.
But for now I would like to have a button that produces a line with a dropdown list of the drugs and the edit fields of the model.
Thank you in advance.
As others have said, a Drug object should not have a quantity associated with it, but a prescription "entry" should.
I think this is the Model structure you need:
QUANTITY_STR = ['Bottle', 'Tablet' 'Injection', 'Capsules', 'other']
class DocAide(models.Model):
# same properties but remove `drugs` from this model
class Drug(models.Model):
# same properties but remove `quantity` property
class Prescription(model.Model):
drug = model.ForeignKey(to=Drug, related_name='prescriptions')
doc_aide = model.ForeignKey(to=DocAide, related_name='prescriptions')
quantity = models.IntegerField()
qty_container = models.CharField(max_length=10, choices=QUANTITY_STR, default=QUANTITY_STR[0])
I changed a few things for you assuming I understood your business logic correctly. Such as how the quantity field works.
I created two fields to describe the quantity. quantity holds the numerical value, and qty_container holds the container's name if you will, like "Bottle", "Injection" and so on.
qty_container has a max_length equal to the number of characters in the word "Injection" since it is the largest word that might fit in this field. You had the default of that field be greater than the max_length which would cause an error.
Now I'm not sure why you wanted to save a tuple of two strings in the quantity field so I ignored that, but if you can comment on your intended logic here I might be able to edit the answer.
Anyway, the Prescription model.
This model will act as an intermediary between Drug and DocAide, and it is the one that will hold the quantity information. I've linked it with Drug and DocAide using foreign keys and set the related_name to suitable names. These "related_names" you'll find show up in the referenced model. So for example if you can do
doc_aide = DocAide.objects.get(pk=1)
for presc in doc_aide.prescriptions:
print(presc.drug.name)
print(presc.quantity)
This means that one DocAide object will be linked with one or many Prescription objects, each of those holds quantity info and is linked with a Drug object:
DocAide (id, etc) >> Prescription (doc_aide_id, drug_id, qty_info) >> Drug (id, etc)
As a side note, after you're sure everything works and you're good to go, you might need to look into query optimization in Django, because as it is written now, it's pretty unoptimized. But don't worry about optimization until you've finished and your code works correctly.
You likely want to move some fields of your Drug model to an intermediary one that defines a foreign key to both your Drug and DocAide models.
This model should then be used as the through option of your DocAide.drugs field.
If you are using the admin you'll be able to rely on inlines to display an input form that allows selecting the desired Drug and annotating extra fields otherwise you'll likely have to build your own mechanism that relies on ModelFormSet.

Using ForeignKey to sort with order_by and distinct not working

I'm trying to sort model Game by each title and most recent update(post) without returning duplicates.
views.py
'recent_games': Game.objects.all().order_by('title', '-update__date_published').distinct('title')[:5],
The distinct method on the query works perfectly however the update__date_published doesn't seem to be working.
models.py
Model - Game
class Game(models.Model):
title = models.CharField(max_length=100)
slug = models.SlugField(unique=True)
description = models.TextField()
date_published = models.DateTimeField(default=timezone.now)
cover = models.ImageField(upload_to='game_covers')
cover_display = models.ImageField(default='default.png', upload_to='game_displays')
developer = models.CharField(max_length=100)
twitter = models.CharField(max_length=50, default='')
reddit = models.CharField(max_length=50, default='')
platform = models.ManyToManyField(Platform)
def __str__(self):
return self.title
Model - Update
class Update(models.Model):
author = models.ForeignKey(User, models.SET_NULL, blank=True, null=True,) # If user is deleted keep all updates by said user
article_title = models.CharField(max_length=100, help_text="Use format: Release Notes for MM/DD/YYYY")
content = models.TextField(help_text="Try to stick with a central theme for your game. Bullet points is the preferred method of posting updates.")
date_published = models.DateTimeField(db_index=True, default=timezone.now, help_text="Use date of update not current time")
game = models.ForeignKey(Game, on_delete=models.CASCADE)
article_image = models.ImageField(default='/media/default.png', upload_to='article_pics', help_text="")
platform = ChainedManyToManyField(
Platform,
horizontal=True,
chained_field="game",
chained_model_field="game",
help_text="You must select a game first to autopopulate this field. You can select multiple platforms using Ctrl & Select (PC) or ⌘ & Select (Mac).")
See this for distinct reference Examples (those after the first will only work on PostgreSQL)
See this one for Reverse Query - See this one for - update__date_published
Example -
Entry.objects.order_by('blog__name', 'mod_date').distinct('blog__name', 'mod_date')
Your Query-
Game.objects.order_by('title', '-update__date_published').distinct('title')[:5]
You said:
The -update__date_published does not seem to be working as the Games are only returning in alphabetical order.
The reason is that the first order_by field is title; the secondary order field -update__date_published would only kick in if you had several identical titles, which you don't because of distinct().
If you want the Game objects to be ordered by latest update rather their title, omitting title from the ordering seems the obvious solution until you get a ProgrammingError that DISTINCT ON field requires field at the start of the ORDER BY clause.
The real solution to sorting games by latest update is:
games = (Game.objects
.annotate(max_date=Max('update__date_published'))
.order_by('-update__date_published'))[:5]
The most probable misunderstanding here is the join in your orm query. They ussually lazy-loading, so the date_published field is not yet available, yet you are trying to sort against it. You need the select_related method to load the fk relation as a join.
'recent_games': Game.objects.select_related('update').all().order_by('title', '-update__date_published').distinct('title')[:5]

Django (Model)Form Field: Manytomany with key value pair

I have a situation where I need to do something similar to rendering a formset within a formset. But I'd rather focus on the problem before jumping to a solution.
In English first:
I'm creating a shipment from a warehouse.
Each shipment can contain multiple lines (unique combinations of product_type and package_type) with an item_count
However for each line there could be multiple "Packages" - a package_type of a product_type that has an item_count. Think of this as a batch.
The customer is only interested in seeing one line for each product_type/package_type
But we need to pull out the stock and correctly attribute the particular units from each batch to allow stock control, recall control etc to function. Therefore the dispatch staff IS interested in exactly which Packages are shipped.
Add to this the sales staff enter a SalesOrder that only specifies the product_type/package_type. They aren't interested in the Packages either. (Think putting in a forward order for next month - who knows what will be in stock then?).
Now the models (simplified for clarity):
class Package(models.Model):
create_date = models.DateField()
quantity = models.FloatField()
package_type = models.ForeignKey(PackageType, on_delete=models.PROTECT)
product_type = models.ForeignKey(ProductType, on_delete=models.PROTECT)
class CheckOut(models.Model):
package = models.ForeignKey(Package, on_delete=models.PROTECT)
create_date = models.DateField()
quantity = models.FloatField()
class Shipment(models.Model):
sales_order = models.ForeignKey(SalesOrder, null=True, blank=True)
ship_date = models.DateField(default=date.today,
verbose_name='Ship Date')
class ShipmentLine(models.Model):
shipment = models.ForeignKey(Shipment, null=True, blank=True)
sales_order_line = models.ForeignKey(SalesOrderLine, null=True, blank=True)
quantity = models.FloatField(verbose_name='Quantity Shipped')
checkout = models.ManytoManyField(CheckOut)
I currently have it working well with the constraint of a 1:M relationship of CheckOut:ShipmentLine. However when changing this to a M:M, things get knarly form-wise.
In the 1:M version the Shipment form (plus formset for the ShipmentLines) looks like this:
class CreateShipmentForm(forms.ModelForm):
class Meta:
model = om.Shipment
contact = forms.ModelChoiceField(
queryset=om.Contact.objects.filter(is_customer=True, active=True),
label='Customer')
customer_ref = forms.CharField(required=False, label='Customer Reference')
sales_order = forms.ModelChoiceField(queryset=om.SalesOrder.objects.all(),
required=False, widget=forms.HiddenInput())
number = forms.CharField(label='Shipment Number', required=False,
widget=forms.TextInput(attrs={'readonly': 'readonly'}))
class CreateShipmentLineForm(forms.ModelForm):
class Meta:
model = om.ShipmentLine
widgets = {
'checkout': forms.HiddenInput()
}
fields = ('package', 'quantity', 'id',
'sales_order_line', 'checkout')
id = forms.IntegerField(widget=forms.HiddenInput())
sales_order_line = forms.ModelChoiceField(
widget=forms.HiddenInput(), required=False,
queryset=om.SalesOrderLine.objects.all())
package = forms.ModelChoiceField(required=True, queryset=None) # queryset populated in __init__, removed for brevity
So for the 1:M, I could select a package, set the quantity and done.
For M:M, I will need to select product_type, package_type, and then 1 or more packages, AND for each package a quantity. (I'll be using JS in the form to filter these)
In my mind's eye I have a few possibilities:
create a (child) formset for the Packages and quantities and include in each line of the (parent) formset
create some sort of multi-field, multi-value matrix custom form field and use that
construct a modal dialog where the M:M stuff happens and somehow save the result to the form where validation, saving happens.
I hope I have explained it correctly and clearly enough. It's the most complex application of Django forms I've encountered and I'm not sure what the limitations/pros/cons of each of my options is.
Has anyone encountered this situation and have a solution? Or any words to the wise?
My thanks in advance,
Nathan
I have a similar situation, I am doing something like your second and third options:
I have overridden __init__() and, after calling super, I have a loop that adds a value selector for every field (of course you could use a single custom element here)
Then override save() and after calling super I process the extra field adding all the values.