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
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 %}
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.
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?
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!
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.