Get minimum of related model date field (django) - python

I have the two following classes:
class Incident(models.Model):
iid = models.IntegerField(primary_key=True)
person = models.ForeignKey('Person', on_delete=models.SET_NULL, null=True)
class Source(models.Model):
sid = models.IntegerField(primary_key=True)
incident = models.ForeignKey('Incident', on_delete=models.SET_NULL, null=True)
url = models.TextField(validators=[URLValidator()])
datereported = models.DateField(null=True, blank=True)
I want to create a field within the Incident that will pull the minimum datereported of related sources. Is this best done in the model, or in the template? Unsure what best practice is, or how to execute in this case.

Is this best done in the model, or in the template?
Well a template should - strictly speaking - not contain business logic. It should contain render logic. It should thus specify how something should be visible, not what should be visible. So it dos not really belong in the template layer, only in the model layer.
You can obtain the smallest datereported with:
from django.db.models import Min
class Incident(models.Model):
iid = models.IntegerField(primary_key=True)
person = models.ForeignKey('Person', on_delete=models.SET_NULL, null=True)
#property
def first_reporteddate(self):
return self.source_set.aggregate(first=Min('datereported'))['first']
This will ignore Sources with datereported set to None (so if there are multiple sources, it takes the smallest datereported that is not None). If there are no Source with a datereported not equal to None, or no related Sources at all, it will return None however, since the minimum of an empty set is considered to be NULL (in SQL, or None in Python/Django).
You can then use this in the template like:
{{ some_incident.first_reporteddate }}
In case you want the entire object, you can use self.source_set.order_by('datereported').first() which will give you the earliest related Source instance. But this will have a (quite) small impact on performance (it will take a bit longer). In that case Django will fetch all columns into memory first. If you thus only need one column, this will result in the fact that you did some useless serialization (at the database end) and deserialization (at the Python end).

You can use model's property for this:
class Incident(models.Model):
iid = models.IntegerField(primary_key=True)
person = models.ForeignKey('Person', on_delete=models.SET_NULL, null=True)
#property
def first_datereported(self):
first_source = self.source_set.order_by('datereported').first()
if first_source:
return first_source.datereported
In template or in any other part of code you can use first_datereported as normal model's field:
{{ incident_instance.first_datereported }}

Related

Django, many to many relationship, how to insert value for all keys in table?

I have 3 models Company, Discount and CompanyDiscountRelation as below:
class Company(models.Model):
name = models.CharField(max_length=150)
def __str__(self):
return self.name
class Discount(models.Model):
name = models.CharField(max_length=150)
discount_value = models.IntegerField()
def __str__(self):
return self.name
class DiscountCompanyRelation(models.Model):
company= models.ForeignKey(Company, on_delete=models.CASCADE)
discount = models.ForeignKey(Discount, on_delete=models.CASCADE)
is_active = models.BooleanField(default=True)
I know how to assign a previously created discount to one company. I do it by DiscountCompanyRelationForm and choose company from form list. But i want to assign discount to all companies by one-click. How to do this? I tried get all ID's by:
Company.objects.values_list('pk', flat=True)
and iterate through them but i don't think that's how it should be done and i have problem to save form by:
form.save()
I tried all day but now I gave up.
Sorry if this is basic knowledge. I've been working with django for a few days.
If I understand the question, you want to choose a subset of companies in the Company table, and apply a particular Discount.
The first can be a ModelMultipleChoiceField, the second a ModelChoiceField. Put these in a form with appropriate querysets for the companies and discount that can be chosen, and when the form validates, apply the discount:
discount = form.cleaned_data['discount']
companies = form.cleaned_data['companies']
for company in companies:
relation = DiscountCompanyRelation(
discount = discount,
company = company,
is_active = # whatever you need
)
relation.save()
You need to think about what happens when a discount is applied to a company which already has a discount. You'll put code in the above loop to check and implement the appropriate action.
I'd strongly recommend specifying a related_name on your ForeignKeys rather than relying on whatever Django generates automagically if you don't.
You might also want to look at the "through" option of a model ManyToManyField, because that's another way to create the same DB structure but brings Django's ManyToMany support code online for you.

How do I limit a django model's field choices using one of the previous fields?

The following is in my models.py:
class SensorType(models.Model):
hardware_type = models.CharField(max_length=100)
is_static = models.BooleanField(default=False)
# Some other fields
class Sensor(models.Model):
device_id = models.CharField(max_length=100, primary_key=True)
sensor_type = models.ForeignKey(SensorType, on_delete=models.PROTECT)
# Some other fields
class Asset(models.Model):
name = models.CharField(max_length=100)
sensor_type = models.ForeignKey(SensorType, on_delete=models.PROTECT) # I need to use this field to filter below
sensor = models.ForeignKey(Sensor, on_delete=models.PROTECT, limit_choices_to={'sensor_type': WHAT DO I PUT HERE?},)
# Some other fields
I need to limit the choices in the sensor field of asset so that only sensors with the sensor_type set in the field immediately above, show up.
The reasoning behind this is that there will eventually be many sensors and it would be very useful to filter this. Initially I only need this to work from the admin page but this will eventually extend when I make my Create and Update Views.
Is this even possible? I'm essentially trying to access attributes before the object has actually been created.
After reading several other questions such as this one I have also looked into ModelChoiceField but the same issue exists of trying to access the form data before it has been submitted.
I'm very open to changing the model structure if that is what is required.

How do I use django GenericForeignKeys?

I have the following two models:
class URLResource(models.Model):
class ResourceType(models.TextChoices):
VID = 'VID', _('Video')
DOC = 'DOC', _('Document')
resource_type = models.CharField(
choices=ResourceType.choices,
default=ResourceType.UNK,
max_length=3)
title = models.CharField(max_length=280, null=True)
url = models.URLField()
created = models.DateTimeField(auto_now_add=True)
def __repr__(self):
return f'URLResource {self.resource_type}: {self.title} URL: {self.url}'
class Record(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
...)
date = models.DateTimeField(default=now, blank=True)
# <resource>: I want to point to a resource somehow
def __repr__(self):
return f'Record {{ User: {self.user}, Resource: }}'
My use case is such that a lot of users can create Records and list them, but since the underlying URLResource is going to be same, I thought I could use many-to-one relationship from Records-to-URLResource so the underlying resources would remain the same. However, even though I have only one type of resource now i.e. URLResource, I also want to roll out other resources like VideoResource, PhysicalResource etc. Clearly, I can't simply use the many-to-one relationship. So, I figured I could use Django's contenttype framework, but I still can't get things to work.
I want to be able to do atleast these two things:
When creating a Record, I can easily assign it a URLResource (or any other Resource) (but only one resource).
I can easily access the URLResource fields. For example like Record.objects.get(user=x).resource.resource_type.

django query compare two models

I have three models, I want to display users who apply for leave but I don't know how to accomplish that. The relationship between user to new leave is one to many and the leave balance to new leave is many to many and user to balance is one to one. I only want to display data from user and new leave.
class newleave(models.Model):
user=models.ForeignKey(User,default='',on_delete=models.CASCADE)
leave_balance=models.ManyToManyField(Leave_Balance)
leave=(
('annual','annual'),
('sick','sick'),
)
Leave_type=models.CharField(max_length=100,choices=leave,blank=False,default='')
dp=(
('test','test'),
('test1','test1'),
)
department=models.CharField(max_length=100,choices=dp,blank=False,default='')
Start_Date=models.DateField(null=True, blank=False, default=None)
End_Date=models.DateField(null=True, blank=False, default=None)
Total_working_days=models.FloatField(null=True, blank=False, default=None)
def __unicode__(self):
return self.Leave_type
class Leave_Balance(models.Model):
user=models.OneToOneField(User,on_delete=models.CASCADE,primary_key=True,)
Outstanding_balance=models.FloatField(null=True, blank=True, default=None)
Monthly_entitlement=models.FloatField(null=True, blank=True, default=None)
Monthly_consumption=models.FloatField(null=True, blank=True, default=None)
Leave_current_balance= models.FloatField(null=True, blank=True, default=None)
Month=models.CharField(max_length=100,default="",null=True)
Year=models.CharField(max_length=100,default='')
def __unicode__(self):
return self.Year
The simple way to get users that have an entry in the newleave table is
User.objects.filter(newleave_set__isnull=False).distinct()
I would suggest following PEP8 for your code style as it allows programmers familiar with Python to quickly pick up the intentions of the programs that you write.
Class names should use the CapsWord convention - class NewLeave class LeaveBalance
Method names and instance variables should be lowercase words seperated by underscores NewLeave.start_date etc
The solution was quite straight forward but it took me a while to figure it out. I access the User models data via foreign key and Django actually took care of the joins.
views.py
def Allleaves(request):
allleave=NewLeave.objects.all()
return render(request,'allleave.html',locals())
and in the allleave.html, i access the the User info through the foreign key "user"
{% for leave in allleave%}
{{leave.user.first_name}}
{{leave.user.last_name}}
{{leave.Leave_type}}
{{leave.department}}
{%endfor%}

How to handle state / conditionally modify fields within Django forms

I have a django model representing a task. This task will go through multiple states from 'DRAFT' to 'COMPLETION' and during that time, various fields will change from requiring user input to explicitly refusing it. For example, you can't change the date a task was completed while it's in draft. Additionally i want different links to appear on the page based on the state (i.e. if it's in checking I want the submit button to say 'Complete this task')
I originally planned for the status to be a model in it's own right but could not work out a way - beyond hard coding - that would bring any value to those models so opted for an explicit list instead. (Any better approaches here would be appreciated).
The key problem I have is how to manage these fields and their states. It seems like the easiest would be to have conditional statements in the template like {% if task.status = 'ACCEPTED' %} but that seems like putting an awful lot of business logic into a template. I also get the impression that disabling a field once you're already in a template is much harder than the form.
My current approach is to use the view to manage these states but that seems messy and doesn't (for me) solve how to change link names and the like in the template.
if task.status = Task.ACCEPTED:
form.fields['datereceived'].disabled = True
if task.status = Task.COMPLETED:
...
Is the view the place to manage these and is there a more pythonic/djangonic to manage these without overloading the template?
Sample code so excuse bugs:
Model
class Task(models.Model):
STATUS_CHOICES = (
(DRAFT, DRAFT),
(ALLOCATED, ALLOCATED),
(ACCEPTED, ACCEPTED),
(CHECKING, CHECKING),
(COMPLETED, COMPLETED),
(WITHDRAWN, WITHDRAWN),
(ON_HOLD, ON_HOLD),
)
status = models.CharField(max_length=20, choices=STATUS_CHOICES,default=DRAFT)
datereceived = models.DateField(blank=True, null=True)
dateworked = models.DateField(blank=True, null=True)
datechecked = models.DateField(blank=True, null=True)
datecompleted = models.DateField(blank=True, null=True)
datedue = models.DateField(blank=True, null=True)
Modelform
class TaskForm(forms.ModelForm):
class Meta:
model = Task
#fields = All fields listed individually but hard to pick out for sample
widgets = {
'datereceived': forms.DateInput(attrs={'class':'datepicker'}),
'datedue': forms.DateInput(attrs={'class':'datepicker'}),
'datecompleted': forms.DateInput(attrs={'class':'datepicker'}),
}
Try putting the logic in the form instantiation code as so:
class TaskForm(forms.ModelForm):
class Meta:
model = Task
def handle_state(self, *args, **kwargs):
task = getattr(self, 'instance', None)
if task:
if task.status = Task.ACCEPTED:
self.fields['datereceived'].disabled = True
elif task.status = Task.COMPLETED:
...
def __init__(self, *args, **kwargs):
super(TaskForm, self).__init__(*args, **kwargs)
self.handle_state(*args, **kwargs)
you can use finite state machine. django-fsm to handle states of your task. In this you can define the source and target state of every transition. for reference you can see this example. https://distillery.com/blog/building-for-flexibility-using-finite-state-machines-in-django/

Categories