I need several models that inherit from a base class in a one-to-one relationship. In keeping with the Django example:
from django.db import models
class Place(models.Model):
name = models.CharField(max_length=50)
class Restaurant(Place):
serves_hot_dogs = models.BooleanField(default=False)
serves_pizza = models.BooleanField(default=False)
class Garage(Place):
car_brands_serviced = Models.ManyToManyField(Brands)
class Boutique(Place):
for = Models.ChoiceField(choices=( ("m", "men"), ("w", "women"), ("k","kids"))
# etc
Now how do I effectively distinguish between the various types of Places when I iterated them in a template (or view function)? Right now, I only see this solution (if I want to iterate over Places, rather than child models individually):
for place in Place.objects.all():
try:
r = place.restaurant
# Do restaurant stuff here
except ObjectDoesNotExist:
try:
g = place.garage
# Do garage stuff here
except ObjectDoesNotExist:
try:
b = place.boutique
# Do boutique stuff here
except ObjectDoesNotExist:
# Place is not specified
Not even sure how that would translate to a template, but this code seems very wrong and inefficient.
As an escape, I guess you could make a choicefield in Place to track which child model is related, but that equates to dangerous denormalisation.
Am I somehow overthinking this? How do you do this?
Could it be something simple like:
models.py:
from django.db import models
class Place(models.Model):
name = models.CharField(max_length=50)
class Restaurant(Place):
serves_hot_dogs = models.BooleanField(default=False)
serves_pizza = models.BooleanField(default=False)
is_restaurant = True
class Garage(Place):
car_brands_serviced = Models.ManyToManyField(Brands)
is_garage = True
A template could work like this – template.html:
{% for place in places %}
{% if place.is_restaurant %}
<!-- Restaurant Stuff -->
{% elif place.is_garage %}
<!-- Garage Stuff -->
{% endif %}
{% endfor %}
Related
I have an intermediate models connection
Simplified:
class Person(models.Model):
first_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
etc...
class Company(models.Model):
name = models.CharField(max_length=60)
etc...
class CompanyEnrollment(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
company = models.ForeignKey(Company, on_delete=models.CASCADE)
company_position =
models.ForeignKey(CompanyPosition,on_delete=models.CASCADE)
etc...
class Meta:
unique_together = [['person', 'company']]
class CompanyPosition(models.Model):
name = models.CharField(max_length=60)
I want to create the following array:
datas = Person.objects.All(...{All elements of Person model supplemented with CompanyPosition_name}...)
There is a case where a person does not have a company_name association
Is it possible to solve this with a query?
If you have an explicit through model for your many-to-many relationship, you could use this to only get or exclude based on the entries of this table, using an Exists clause:
enrollments = CompanyEnrollment.objects.filter(person_id=OuterRef('pk'))
enrolled_persons = Person.objects.filter(Exists(enrollments))
unenrolled_persons = Person.objects.filter(~Exists(enrollments))
If you don't have an explicit intermediate table, then it's has been generated by Django directly. You should be able to use it by referring to it with Person.enrollments.through instead of CompanyEnrollment.
Since you did not detailed much the CompanyEnrollment table, I assumed you're in the second case.
This is not the best solution in my opinion, but it works for now, with little data. I think this is a slow solution for a lot of data.
views.py I will compile the two necessary dictionaries
datas = Person.objects.all().order_by('last_name', 'first_name')
companys = CompanyEnrollment.objects.all()
Use Paginator
p = Paginator(Person.objects.all(), 20)
page = request.GET.get('page')
pig = p.get_page(page)
pages = "p" * pig.paginator.num_pages
Render HTML:
context = {
"datas": datas,
"pig": pig,
"pages": pages,
"companys": companys,
}
return render(request, "XY.html", context)
HTML template:
{% for datas in pig %}
{{datas.first_name}} {{datas.last_name}}
{% for company in companys %}
{% if datas == company.person %}
{{company.company.name}} <br>
{% endif %}
{% endfor %}
{% endfor %}
not the most beautiful, but it works ... I would still accept a more elegant solution.
I have models that inherit from an abstract model like this:
class ImprovementAbstraction(models.Model):
needsImprovement = models.BooleanField()
hasFallacies = models.BooleanField()
hasEmotionalDeficiency = models.BooleanField()
isNecessaryToObtain = models.BooleanField()
doesAReallyCauseB = models.BooleanField()
areAllStepsPresent = models.BooleanField()
isCauseSufficient = models.BooleanField()
areAllClausesIdentified = models.BooleanField()
isCausalityCertain = models.BooleanField()
languageIsEnglish = models.BooleanField()
isTautologyPresent = models.BooleanField()
class Meta:
abstract = True
class Assumption(MainAbstractModel, ImprovementAbstraction):
need = models.ForeignKey(Need, on_delete=models.SET_NULL, null=True)
assumption = models.CharField(max_length=500, default="", null=True)
def __str__(self):
return self.assumption
In the template I would like to display all of the "ToImprovementAbstraction" Model fields associated with the Assumption model. Is there a way to loop over all the fields in the template, something like Assumption.ImprovementAbstractionFields.all() (made up code)?
I use the built-in vars() method for that.
For example, you have an Assumption object:
assumptionObject = .models.Assumption.objects.get(pk=1)
If you use vars() method with that query object like this:
vars(assumptionObject)
it will return a dictionary containing all the field names and values as a Python dictionary.
If you only want the field names you can use it like this:
vars(assumptionObject).keys()
EDIT: I should warn you that, if you use vars() on a query object, the returned dictionary will contain a django.db.models.base.ModelState object stored in a key called _state. If you're going to use the values in a for loop or something, you should put an exception for that.
Not exactly sure what your desired outcome is, but this is the basic approach to query data from the database and render the html:
You will first have to query the data from the database like so:
Views.py
def search(request):
queryset = ToImprovementAbstraction.objects.all()
context = {
'queryset': queryset
}
return render(request, 'your_template.html', context)
Then you can use the data to render your template like so:
your_template.html
{% for item in queryset %}
<p>{{ item.needsImprovement }}</p>
[...]
[...]
{% endfor %}
If oyu have multiple models you can make multiple queries to use one/many-to-many fields in your models to link them straight away.
get the assumption object in view and render the ImprovementAbstraction through foreign key. for example:
def get_item():
queryset = Assumption.objects.all()
return render(request, 'template.html', {'queryset': queryset})
Now in your template you can access the data this way
{% for query in queryset %}
{{query.need.needImprovement}}
{{query.need.hasFallacies}}
{{...}}
{% endfor %}
This way you can display everything in one loop. Hope this gets you some idea.
To illustrate the problem I propose to consider a simplified version of my application.
Suppose there is a product model:
# products/models.py
from django.db import models
class Product(models.Model):
name = models.CharField(max_length=128)
retail_price = models.DecimalField(max_digits=8, decimal_places=2)
And custom user model:
# authentication/models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
class ClientType(models.Model):
name = models.CharField(max_length=128)
part_of_retail_price = models.DecimalField(max_digits=4, decimal_places=3)
class Client(AbstractUser): # custom user model
client_type = models.ForeignKey(ClientType)
I want to be able to get a special price for a certain type of user in the template:
{% for product in products %}
{{ product.user_price }}
{% endfor %}
Authorized users price is equal to the product of product.retail_price and request.user.client_type.part_of_retail_price, for unauthorized just product.retail_price.
What is the best way to implement it? I would appreciate any hints and help.
If you only need the value for displaying one or a few Client instances at a time, easiest is to use a template filter like {{ product.user_price|user_price }} as suggested on the comment by #Selcuk.
If you need the value in a QuerySet to work with (sorting etc.), then use a Manager, together with annotate() and the ExpressionWrapper().
class ProductManager(models.Manager):
def for_user(self, user):
# Calculate the price here
user_price = user.user.client_type.part_of_retail_price
return self.annotate(user_price=ExpressionWrapper(
Value(user_price), output_field=FloatField()))
class Product(models.Model):
# ...
objects = ProductManager()
Then, when you load the Product QuerySet in your view, add the current user
products = Product.objects.all().for_user(request.user)
to add the user_price to a regular QuerySet, and you can use it in your templates as required.
{% for product in products %}
{{ product.user_price }}
{% endfor %}
more on Django Managers
more on annotations and expressions
I have 2 models:
class Professors(models.Model):
professors_name = models.CharField('professor', max_length=32, unique=True)
class Discipline(models.Model):
auditorium = models.IntegerField('auditorium')
professors_name = models.ForeignKey(Professors)
In views:
disciplines = Discipline.objects.all()
So, I have number of auditorium and professors_name_id. But I need full profrssors name, not id. How to do it?
Models:
# models usually named in the singular
class Professor(models.Model):
professors_name = models.CharField('professor', max_length=32, unique=True)
class Discipline(models.Model):
auditorium = models.IntegerField('auditorium')
# your pointer is to a professor, not to the name
professor = models.ForeignKey(Professor)
In view:
# select_related('professor') to avoid a second query when accessing professor
disciplines = Discipline.objects.select_related('professor')
Template:
{% for disc in disciplines %}
{{ disc.auditorium }}: {{ disc.professor.name }}
{% endfor %}
For values:
Discipline.objects.values('auditorium', 'professor__name')
Django ORM will always returns the objects not the ids. You should have a design like this.
class Professor(models.Model):
name = models.CharField('professor', max_length=32, unique=True)
class Discipline(models.Model):
auditorium = models.IntegerField('auditorium')
professor = models.ForeignKey(Professors)
and use discipline.professor.name to retrieve the name alone.
I have the following models:
class Post(models.Model):
message = models.TextField()
(etc.)
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
(etc.)
class PostFollow(models.Model):
post = models.ForeignKey(Post, related_name='follower_set')
follower = models.ForeignKey(UserProfile, related_name='follower_set')
creation_date = models.DateTimeField(auto_now_add=True)
an_arbitrary_score = models.IntegerField(default=0)
(etc.)
class Meta:
unique_together = ('post', 'follower',)
In my template, I'd like to render a list of posts along with a "follow" or "unfollow" link so that the current user can decide whether to follow a given post. In a world where I could use arguments in Django templating, I'd do something like this:
{% for post in post_set %}
<...stuff...>
{% if post.user_is_following user %}unfollow{% else %}follow{% endif %}
<...more stuff...>
{% endfor %}
However, I can't do that. And I can't make a zero-argument, template-callable method on any of these models, because they all need to know at least one other argument to answer the question whether a given PostFollow row exists in that table.
I'm happy to write a templating extension, but before I bring out the big guns, is this an appropriate case for doing so? Or is there a more Djangoesque solution?
Template filters are not big guns:
# your_app/templatetags/following.py
from django import template
register = template.Library()
#register.filter
def is_followed_by(post, user):
return post.is_followed_by(user)
and then:
{% load following %}
...
{% if post|is_followed_by:user %} ... {% endif %}
You can also put all logic in template filter, remove 'post.is_followed_by' method and use the filter instead of model method just like any other function, #register.filter decorator doesn't harm the decorated function.