How to Map a Django model field to some user defined value - python

Hi My project works on Django and some AngularJS. I want map my Django model field values into some custom string values. The following is my Model,
class History(models.Model):
TYPE_CHOICES = (
(1, 'CREATE'),
(0, 'EDIT'),
(2, 'DELETE'),
)
user = models.ForeignKey(User, related_name='+')
mw = models.ForeignKey('CP', related_name="history")
field_name = models.CharField(max_length=192, null=False)
old_value = models.CharField(max_length=500, null=True, blank=True)
new_value = models.CharField(max_length=500, null=True, blank=True)
type = models.IntegerField(default=0, choices=TYPE_CHOICES)
date_created = models.DateTimeField(auto_now_add=True)
the type 1, 2 and 0 I need to display in my website. So i just passed the value as <td>{{t.type}}</td> But it giving me the values as 1, 2 and 0. How can I display it as create, update or delete string values? Any idea guys? Thanks in advance.

In your template change {{t.type}} to {{ t.get_type_display }}.
You can read about this in docs: get_FOO_display docs

Please take note that if you mixed django template language and some other template language like (angularjs) there will be a different outcome.
So i assume you are using angular? if thats the case
try this
<!-- if using angular js -->
{% verbatim %}
{{ data.angular_t }} {{ angularvariablescope }}
{% endverbatim %}
<!-- all django varibale -->
{{t.type}} to {{ t.get_type_display }}. {# django variable #}
or on your model.py
#property
def angular_t(self)
return self.get_type_display()
retrieve that property on your resource (tastypie)
class Resource(ModelResource):
angular_t = fields.CharField(attributes='angular_t', null=True)

Related

Django How do I filter querysets to insersect common results

I want to filter a queryset that depends on another queryset
My models.py
class Escola(models.Model):
id = models.AutoField(db_column='ID', primary_key=True)
nome = models.CharField(db_column='Nome', max_length=255, blank=True, null=True)
class Inscrio(models.Model):
id = models.AutoField(db_column='ID', primary_key=True)
escolaid = models.ForeignKey(Escola, models.DO_NOTHING, db_column='EscolaID', blank=True, null=True)
This is my views.py
def view_forms(request):
return render(request,
"main/view_forms.html",
{"escolas": Escola.objects.all,
})
I have an .html template where I run a for loop to write all the objects of Escola and those objects are connected with the Model Inscrio (by 'escolaid' foreign key).
I want a queryset to find Inscrio objects that have the same 'escolaid' as Escola's 'id'
For example, say the for loop returned the 'id' = 1 for the model Escola, now I want to get (in a for loop) all Inscrio objects with 'escolaid' = 1 and I want this until the Escola loop has no more values
How can I do that?
Thanks in advance
simple in the template, and if you assigned a related_name(exp: inscrios) for escolaid in Inscrio Model, escola.inscrio_set should be replaced as escola.inscrios. Go through the document: related-objects-reference
{% for escola in escolas %}
{# handle escola #}
{% for inscrio in escola.inscrio_set %}
{# handle inscrio #}
{% endfor %}
{% endfor %}

How to display relationship with back reference at django template through User?

I have such models
class Employee(models.Model):
"""Employee information."""
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='employee', unique=True)
position = models.CharField("current position in a company", max_length=64, blank=True)
birth_date = models.DateField("date of birth", null=True)
skills = models.ManyToManyField(
Technology, through="Skill", verbose_name="skills", blank=True)
class Technology(models.Model):
"""Technologies."""
name = models.CharField('technology name', max_length=32, unique=True)
class Skill(models.Model):
"""Information about an employee's skills."""
LEVELS = (
('basic', 'Basic'),
('intermediate', 'Intermediate'),
('advanced', 'Advanced'),
('expert', 'Expert'),
)
employee = models.ForeignKey(
Employee, on_delete=models.CASCADE, related_name="employee_skills")
technology = models.ForeignKey(Technology, on_delete=models.CASCADE)
start_date = models.DateField(
verbose_name='Works with since:')
level = models.CharField("level", max_length=64, choices=LEVELS)
And I can't understand why my template code doesn't work
template.html
{{ user.employee.position }}
{{ user.employee.birth_date }}
{{ user.employee.summary }}
{% for i in user.employee.skills.all %}
{{ i.technology.name }}
{{ i.level }}
{% endfor %}
I can't see absolutely nothing. All models possible to see at adminpanel. And else then I using TemplateView such as
class AccountView(TemplateView):
template_name = "profile.html"
def get_context_data(self, **kwargs):
context = super(AccountView, self).get_context_data(**kwargs)
context['skills'] =
Skill.objects.filter(employee__user=self.request.user)
return context
then everything works fine.
There is something wrong with the modeling. You should use a OneToOneField between Employee and User. In essence an OneToOneField is a unique ForeignKey. It will however change some logic, such that user.employee will access the related Employee object, not a QuerySet of Employees:
class Employee(models.Model):
# ...
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='employee'
)
# ...
In your AccountView, you unified 'skills' with the skills of that employee, indeed:
class AccountView(TemplateView):
template_name = "profile.html"
def get_context_data(self, **kwargs):
context = super(AccountView, self).get_context_data(**kwargs)
context.update(
skills=Skill.objects.filter(employee__user=self.request.user).select_related('technology')
)
return context
You might want to use .select_related(..) here to prevent the so-called "N+1 problem" where for each skill, you will make an extra query.
So you can render the skills with:
{% for skill in skills %}
{{ skill.technology.name }}
{{ skill.level }}
{% endfor %}
or you can access is through:
{% for skill in request.user.employee.employee_skills.all %}
{{ skill.technology.name }}
{{ skill.level }}
{% endfor %}
although the above is less safe, since it is possible that a User has no related Employee object.
Your Employee model currently has a one to many relationship with a user
class Employee(models.Model):
"""Employee information."""
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='employee', unique=True)
according to your comment you need a one to one relation ship so you need to change this to use a OneToOneField instead of a ForeignKey
Conceptually, this is similar to a ForeignKey with unique=True, but the “reverse” side of the relation will directly return a single object.

Simplifying code with multiple loops and logic, into queryset aggregates

My models:
class customer(models.Model):
cstid = models.AutoField(primary_key=True, unique=True)
insurance_number = models.CharField(max_length=100, blank=True, null=True)
name = models.CharField(max_length=35)
ageyrs = models.IntegerField(blank=True)
class Admission(models.Model):
id = models.AutoField(primary_key=True, unique=True)
clinic = models.ForeignKey(Clinic, on_delete=models.CASCADE)
customer = models.ForeignKey(customer, on_delete=models.CASCADE)
diagnosis = models.CharField(max_length=2000, default='', blank=True)
date_admission = models.DateTimeField(default=timezone.now)
ward = models.ForeignKey(Ward, on_delete=models.CASCADE)
bed = models.ForeignKey(Bed, on_delete=models.CASCADE)
discharged = models.BooleanField(default=False)
ip_number = models.IntegerField(blank=True)
ip_prefix = models.CharField(max_length=20, default='', blank=True)
My objective: Set a variable to a query filter, adding a property, 'is_admitted' to the queryset, so that I can pass this query set to the template and use the property while displaying data.
Code:
def is_admitted(cust):
admission = Admission.objects.filter(customer=cust, discharged=False)
admission_results = len(admission)
if admission_results > 0:
return True
return False
my_q = or_q_if_truthfull(
cstid=HospitalID,
name__lower__contains=name.lower() if name else None,
ageyrs=ageyrs if ageyrs.isdigit() else None,
agemnths=agemnths if agemnths.isdigit() else None,
mobile__contains=mobile if mobile else None,
alternate__contains=alternate if alternate else None,
email__lower__contains=email.lower() if email else None,
address__lower__contains=address.lower() if address else None,
city__lower__contains=city.lower() if city else None
)
ORSearchResult = customer.objects.filter(my_q, linkedclinic=clinicobj)
cust_set = []
cust_admission_status = []
for cust in ORSearchResult:
cust_set.append(cust)
cust_admission_status.append(is_admitted(cust))
print(f"Customer name: {cust.name} Admission status: {is_admitted(cust)}")
cust_templ_set = zip(cust_set, cust_admission_status)
And in template, I will do:
{% for cust, status in cust_templ_set %}
{{ cust.name }} {{ status }}
{% endfor %}
I want to understand how I can convert my above code by generating an aggregate over the queryset, so that I can use a property of the query, and change the template code to the following, and avoid the loop in the view, and the zip. So that the template code becomes:
{% for cust in customers %}
{{ cust.name }} {{ cust.is_admitted }}
{% endfor %}
I am not sure whether I am making complete sense, and can explain further.
I'm not sure I understood you right, perhaps you might want this:
cust = customer.objects.filter(my_q, linkedclinic=clinicobj)
is_admitted_sub_q = Admission.objects.filter(customer=OuterRef('pk'), discharged=False)
cust_templ_set = cust.annotate(is_admitted=Exists(is_admitted_sub_q), )
this will return a list of customers with additional field is_admitted which will be True if there exists at least one linked (to this customer) record in Admission.
OuterRef, Exists
One option could be to use conditional-expressions together with annotate(). It could look like this:
from django.db import models
qs = Customer.objects.filter(...) # your filter conditions go here
# now add a field to the resulting queryset
qs = qs.annotate(
active_admissions=models.Count(
models.Case(
models.When(admission__discharged=False, then=1),
output_field=models.IntegerField())))
Now each object in the queryset will have an additional attribute called active_admissions which will contain the number of active admissions.
This could be used in the template like this:
{% for cust in qs %}
{{ cust.name }} {{ cust.active_admissions }}
{% endfor %}
Maybe you need to modify the subquery to fit your specific needs. Does that help?

Django nested prefetch_related

I'm using nested prefetching to link all my objects with each other and then pushing them to a template to show them.
My general setup: I have frameworks, categories and controls. A control is linked to a category, a category is linked to a framework. A category can also have a supercategory, in which case the category has a call to another category object. Every control also has an initial score, current score and a target score.
What I want to do is for every framework, for every supercategory, for every subcategory show every control with their scores.
Example:
Framework A
Super Category 1
Subcategory 1
Control 1
Initial
Current
Target
Control 2
...
Subcategory 2
Control 3
...
Super Category 2
Subcategory 3
Control 4
...
Framework B
...
I've already managed to do this by using a lot of nested prefetches.
My models:
# models.py
#==============[FRAMEWORKS]==============#
class Framework(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(null=True)
def __str__(self):
return self.name.replace(' ', '_')
class FrameworkCat(models.Model):
name = models.CharField(max_length=100)
identifier = models.CharField(max_length=10,
null=True,
blank=True,
verbose_name="optional identifier")
framework = models.ForeignKey(Framework,
on_delete=models.CASCADE,
verbose_name="related framework")
superCat = models.ForeignKey('self',
on_delete=models.CASCADE,
verbose_name="super category",
blank=True,
null=True)
hexColor = models.CharField(max_length=10,
null=True,
blank=True,
verbose_name="hex color")
def __str__(self):
return self.name
#==============[CONTROLS]==============#
class Control(models.Model):
title = models.CharField(max_length=250)
description = models.TextField()
framework = models.ForeignKey(Framework,
on_delete=models.CASCADE,
verbose_name="related framework")
category = models.ForeignKey(FrameworkCat,
on_delete=models.CASCADE,
verbose_name="related category",
null=True)
def __str__(self):
return self.title
class ProjectScore(models.Model):
project = models.ForeignKey(Project,
on_delete=models.CASCADE,
verbose_name="related project")
control = models.OneToOneField(Control,
on_delete=models.CASCADE,
verbose_name="related control")
framework = models.ForeignKey(Framework,
on_delete=models.CASCADE,
verbose_name="related framework")
score = models.IntegerField(default=-1)
current_score = models.IntegerField(default=-1)
target = models.IntegerField(default=-1)
def __str__(self):
return "Project Score"
My (testing) view:
# views.py
def testing_models(request):
def_data = default_data(request)
frameworks = Framework.objects.prefetch_related(Prefetch('frameworkcat_set',
queryset=FrameworkCat.objects.prefetch_related(Prefetch('frameworkcat_set',
queryset=FrameworkCat.objects.prefetch_related(Prefetch('control_set',
queryset=Control.objects.select_related('projectscore').order_by('id'),
to_attr='controls')).exclude(superCat=None),
to_attr='categories')).filter(superCat=None),
to_attr='supercategories')).all()
def_data['frameworks'] = frameworks
return render(request, 'testing_models.html', def_data)
And my template to show everything:
# templates/testing_models.html
{% for framework in frameworks %}
{% for supercategory in framework.supercategories %}
<p><strong>{{ supercategory.name }}</strong></p>
{% for subcategory in supercategory.categories %}
<p>{{ subcategory.name }}</p>
{% for control in subcategory.controls %}
<p>{{ control.title }}</p>
<p>{{ control.projectscore.score }}</p>
<p>{{ control.projectscore.current_score }}</p>
<p>{{ control.projectscore.target }}</p>
{% endfor %}
{% endfor %}
{% endfor %}
{% endfor %}
This all works correctly by executing 6 queries. Now, my main question is: is there a better way to fetch these objects and link them than using all these nested prefetches? Is there some other kind of syntax that I could be using that makes it easier to read and easier to modify? I have many more things I would like to link but I'm afraid the python statement would become too big and my overview would be lost.
Much appreciated!

Django dealing with a model fields

I'm new to Django and I'm trying to learn as I go. And I've ended up in a situation where I can't figure out what is the best way forward.
snippet from models.py:
class ProjectMeta(models.Model):
project = models.ForeignKey(Project)
architect = models.CharField(max_length=200)
landscape = models.CharField(max_length=100, blank=True)
engineer = models.CharField(max_length=200, blank=True)
client = models.CharField(max_length=100)
consultant = models.CharField(max_length=100, blank=True)
size = models.DecimalField(max_digits=5, decimal_places=2, blank=True)
location = models.CharField(max_length=200)
date = models.DateField()
STATUS = (
('CP', 'Competition'),
('UC', 'Under construction'),
('CO', 'Completed'),
)
status = models.CharField(max_length=2, choices=STATUS, default=1)
And this is the view:
class ProjectDetailView(DetailView):
model = Project
def get_context_data(self, **kwargs):
context = super(ProjectDetailView, self).get_context_data(**kwargs)
context['projectmeta_list'] = ProjectMeta.objects.all()
return context
But if I want to output ProjectMeta in the template I could iterate over projectmeta_list.
{% for metadata in projectmeta_list %}
<p>Architect: {{ metadata.architect }}</p>
{% endfor %}
But this require alot of repeating myself, and I wont work. Because lets say the architect field is empty, I would get Archiect: printed to the page. Is there a built-in way of converting a model into a dict or list, so I can iterate over it and only print out fields that aren't empty to the page?
I've been looking at get_fields(), would that work? https://docs.djangoproject.com/en/1.10/ref/models/meta/#retrieving-all-field-instances-of-a-model
I tried this in the shell, threw me and AttributeError:
>>> from projects.models import *
>>> Project._projectmeta.get_fields()
You should try wrapping the <p>Architect: {{ metadata.architect }}</p> piece in a conditional {% if metadata.architect != '' %} or some condition to that effect.
Try with another ProjectMeta model. Take a look at this one.
class ProjectMeta(models.Model):
project = models.ForeignKey(Project)
name = models.CharField(max_length=50)
value = models.TextField()
And this query should work. myproject.projectmeta_set.filter(name="status")
You can use built-in default or default_if_none template filters to show a default value if it is None or empty string.
{% for metadata in projectmeta_list %}
<p>Architect: {{ metadata.architect|default:"-" }}</p>
{% endfor %}
Check this for more details.

Categories

Resources