Saving previous date and adding new date in Django - django

I have been working on an application where user adds his tasks which he is supposed to perform, once the task is added he can update the progress.
In my model I have a date field, where user is supposed to enter the estimated completion date.
My task model
"""Creating KRA Based on Institutional Objectives"""
class KraBasedOnIo(models.Model):
io = models.ForeignKey(InstitutionalObjectives, on_delete=models.CASCADE, related_name='kra_io')
kra_title = models.CharField(max_length = 200)
kra_description = models.TextField()
kra_target = models.CharField(max_length=200)
kra_added_date = models.DateTimeField(auto_now_add=True)
estimated_date = models.????
While updating the progress, if the user wants to extend his timeline, I am looking for an option where I can save his current estimated completion date and add the new date also.
And when I am displaying his progress I want to show his defined completion date and also the extended completion date in the template.
I have tried a work around with model.JSONField() but couldn't reach there.
There is no ListField or DictionaryField so what could be a better solution for this?

So I would use the library django-simple-history to keep track of the different updates of this field.
"""Creating KRA Based on Institutional Objectives"""
class KraBasedOnIo(models.Model):
io = models.ForeignKey(InstitutionalObjectives, on_delete=models.CASCADE, related_name='kra_io')
kra_title = models.CharField(max_length = 200)
kra_description = models.TextField()
kra_target = models.CharField(max_length=200)
kra_added_date = models.DateTimeField(auto_now_add=True)
estimated_date = models.DateTimeField(default=timezone.now)
history = HistoricalRecords()
Then in you view, you can see the different version by doing :
for record in kra_based_on_info.history.all():
print(record)

Keep it simple and create two separated fields:
class KraBasedOnIo(models.Model):
# ...
estimated_completion_date = models.DateTimeField(null=True, blank=True)
extended_completion_date = models.DateTimeField(null=True, blank=True)
If you need to keep track of all the completion date changes, I suggest you to create a new model
class CompletionDate(models.Model):
# ...
kra = models.ForeignKey(KraBasedOnIo, related_name='completion_dates', on_delete=models.CASCADE)
estimated_date = models.DateTimeField(auto_now_add=True)
And then get the last completion date for a KraBasedOnIo instance like this:
my_last_completion_date = KraBasedOnIo.completion_dates.last()

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 order_by using a conditional

I need to order my site based on if a field is not null. There are two fields that refer to a deadline, the main full deadline field which is a required field, and a trials field which is optional. All entries will use deadline eventually, but some will have a trials date in there and that will be the one that needs to take priority untill that date is past when it should then default to the deadline.
To try to cover just the basics or using one or the other, without it changing when the trials date is passed, I tried the following:
class Project(models.Model):
'''
Main Project, serves the default Projects Portal window.
'''
published = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
area = models.ForeignKey(
Area,
related_name="project",
on_delete=models.PROTECT
)
title = models.CharField(max_length=128, unique=True)
slug = models.SlugField(max_length=64)
summary = models.CharField(max_length=256)
others = models.CharField(max_length=128, blank=True)
staff_trials = models.DateField(null=True, blank=True)
deadline = models.DateField()
slip = models.BooleanField(default=False)
class Meta:
ordering = ["-slip", "staff_trials", "deadline"]
def __str__(self):
return self.title
It was a bit of a stab in the dark to hope it would work, but that just puts all the staff_trials first, which is not quite what I'm after, I need the dates themselves to be in order, e.g.
project A - deadline - 12/01/2020
project B - staff_trials - 15/01/2020 deadline - 01/02/2020
project C - deadline - 20/01/2020
project D - deadline - 23/01/202
So this would be the order and when the 15th is passed Project B would appear at the bottom of this list. Is there a way to do this?
I think you may not be able to define this ordering on model's Meta class, but while filtering instances, you can follow an approach like the following:
Projects.objects.annotate(
actual_deadline=Case(
When(staff_trials__isnull=True, then=F('deadline')),
When(staff_trials__isnull=False, then=F('staff_trials')),
output_field=DateTimeField(),
)
).order_by('actual_deadline')
Here we annotate each result with a DateTimeField named "actual_deadline", getting the valie of staff_trials if defined, deadline field if not defined. Then we order the results with this field.
I haven't tested the code but should work with some tweaks if not directly.

Is there any possible solution for getting more than one value inside function in django?

I am creating a blog application using Django and I am also very much new to django.
This is the models I created
class categories(models.Model):
Title = models.CharField(max_length=40, default='GST')
class Blog(models.Model):
User = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,null=True,blank=True)
Date = models.DateTimeField(default=datetime.now)
Blog_title = models.CharField(max_length=255)
likes = models.ManyToManyField(settings.AUTH_USER_MODEL,related_name='likes',blank=True)
Description = RichTextUploadingField(blank=True, null=True,config_name='special')
Blog_image = models.ImageField(upload_to='blog_image', null=True, blank=True)
Category = models.ForeignKey(categories,on_delete=models.CASCADE,related_name='blogs')
I was wondering How to count the total no of blog present under a particular category?
I want to track a specific count rate for all Categories...
Done something like this in my model
def categories_count(self):
for a in categories.objects.all():
categories_count = Blog.objects.filter(Category__Title=a.Title).count()
return categories_count
But it is returning only one value...Can anyone suggest me with some suitable codes to resolve this...
Thank you
You can get a list of tuples of category title and blog count with the following query:
categories.objects.annotate(blog_count=Count('Categories')).values_list('Title', 'blog_count')

Efficient alternative at Django filter by property

I know that filtering by property is not possible with Django, as filtering is done at database level and properties live in Python code. However, I have the following scenario:
In one hand, I have the model RegisteredUser on the other hand Subscription. A user can have multiple subscriptions, a subscription is from one user and a user has one or none active subscriptions.
To implement this, I have a foreign key from Subscription to RegisteredUser and a property subscription at RegisteredUser that points to the active one (latest created subscription for that user) or none if he hasn't any subscriptions.
Which would be the most efficent way to filter users that have subscription "platinum", "gold", "silver"...? I could do a "fetch all subscriptions" and then iterate over them to check each one for a match. But it would be really expensive and if I have to do the same process for each kind of subscription type, then cost would be s * u (where s is the number of different subscriptions and u is the number of users).
Any help will be appreciated. Thanks in advance!
UPDATE:
When I first explained the problem, I didn't include all the models related to
simplify a litte. But as you are asking me for the models and some of you haven't understood me
(perhaps I wasn't clear enough) here you have the code.
I've simplified the models and stripped out code that is not important now.
What do I have here? A RegisteredUser can have many subscriptions (because he may change it
as many times as he wants), and a subscription is from just one user. The user has only
one current subscription, which is the latest one and is returned by the property
subscription. Subscription is attached with Membership and this is the model whose
slug can be: platinum, gold, silver, etc.
What do I need? I need to lookup Content whose author has a specific kind of membership.
If the property approach worked, I'd have done it like this:
Content.objects.filter(author__id__in=RegisteredUser.objects.filter(
subscription__membership__slug="gold"))
But I can't do this because properties can't be used when filtering!
I thought that I could solve the problem converting the "virtual" relation created by
the property into a real ForeignKey, but this may cause side effects, as I should update it manually each time a user changes its subscription and now it's automatic! Any better ideas?
Thanks so much!
class RegisteredUser(AbstractUser):
birthdate = models.DateField(_("Birthdate"), blank=True, null=True)
phone_number = models.CharField(_("Phone number"), max_length=9, blank=True, default="")
#property
def subscription(self):
try:
return self.subscriptions_set.filter(active=True).order_by("-date_joined",
"-created")[0]
except IndexError:
return None
class Subscription(models.Model):
date_joined = models.DateField(_("Date joined"), default=timezone.now)
date_canceled = models.DateField(_("Date canceled"), blank=True, null=True)
subscriber = models.ForeignKey(AUTH_USER_MODEL, verbose_name=_("Subscriber"),
related_name="subscriptions_set")
membership = models.ForeignKey(Membership, verbose_name=_("Membership"),
related_name="subscriptions_set")
created = models.DateTimeField(_("Created"), auto_now_add=True)
last_updated = models.DateTimeField(_("Last updated"), auto_now=True)
active = models.BooleanField(_("Active"), default=True)
class Membership(models.Model):
name = models.CharField(_("Name"), max_length=15)
slug = models.SlugField(_("Slug"), max_length=15, unique=True)
price = models.DecimalField(_("Price"), max_digits=6, decimal_places=2)
recurring = models.BooleanField(_("Recurring"))
duration = models.PositiveSmallIntegerField(_("Duration months"))
class Content(models.Model):
author = models.ForeignKey(AUTH_USER_MODEL, verbose_name=_("Author"),
related_name="contents_set")
title = models.CharField(_("Title"), max_length=50)
slug = models.SlugField(_("Slug"), max_length=70, unique=True)
content = RichTextField(_("Content"))
date = models.DateField(_("Date"), default=timezone.now)
published = models.BooleanField(_("Published"))
Finally, to solve the problem I replaced the subscription property by a real foreign key and added a signal to attach the RegisteredUser with the created subscription.
Foreign key:
subscription = models.ForeignKey(Subscription, verbose_name=_("Subscription"),
related_name='subscriber_set', blank=True, null=True)
Signal:
#receiver(post_save, sender=Subscription)
def signal_subscription_post_save(sender, instance, created, **kwargs):
if created:
instance.subscriber.subscription = instance
instance.subscriber.save()
I think you model are something like:
KIND = (("p", "platinum"), ("g","gold"), ("s","silver"),)
class RegisteredUser(models.Model):
# Fields....
class Subscription(models.Model):
kind = models.CharField(choices=KIND, max_len=2)
user = models.ForeignKey(RegisteredUser, related_name="subscriptions")
Now, you can do something like that:
gold_users = RegisteredUser.objects.filter(subscriptions_kind="g")
silver_users = RegisteredUser.objects.filter(subscriptions_kind="s")
platinum_users = RegisteredUser.objects.filter(subscriptions_kind="p")
Adapt it to your models
Hope helps
EDIT
Now, With your models, I think you want something like:
content_of_golden_users = Content.objects.filter(author__subscriptions_set__membership__slug="golden")
content_of_silver_users = Content.objects.filter(author__subscriptions_set__membership__slug="silver")
content_of_platinum_users = Content.objects.filter(author__subscriptions_set__membership__slug="platinum")

In Django, I want to be able to bulk create items with a foreign key to an object within its admin form. How do I do this?

So I am working on a course syllabus app. Each syllabus has one or more class meeting times, a start date, an end date. I would like to be able to automatically generate a set of class meetings for each syllabus based on this information within the admin form. The idea is simply that the user should be able to input the meeting times, start date, and end date, hit a button, and have django create inline class session objects that the user can then edit. So, for instance, if I have a class that meets every Tuesday from 1 - 3 between May 1 and September 1, I should be able to hit a button, and get class sessions--or at least class session forms, there's no reason to save them in the database until they're filled in--for every Tuesday between those dates.
The relevant model code is as follows:
class Syllabus (models.Model):
name = models.CharField(max_length=120)
number = models.CharField(max_length=32, blank = True)
start_date = models.DateField()
end_date = models.DateField()
class DaysTime(models.Model):
days = MultiSelectField(max_length=15, choices=WEEKDAYS)
startTime = models.TimeField()
endTime = models.TimeField()
class Meta:
abstract = True
class ClassTime(DaysTime):
key = models.ForeignKey('Syllabus')
class EventAbstract (models.Model):
name = models.CharField(max_length=120)
description = models.TextField(blank = True)
startDateTime = models.DateTimeField()
endDateTime = models.DateTimeField(blank = True)
class Meta:
abstract=True
class ClassSession(EventAbstract):
cancelled = models.BooleanField(default = False)
syllabus = models.ForeignKey('Syllabus')
date = models.DateField()
Generating the dates and the ClassSessions for the dates isn't the problem. Adding a button onto a custom admin template to extend change_form isn't a problem either. Because the change_form is highly generic, and does not know anything about the models it is presenting, there's no way I can see to make the button talk to the function.
So as I see it the problem is three fold:
How do I supply the recurring class times to the function that generates the new ClassSessionInlines?
How do I create them on the admin page?
How do I populate their fields?