Django relationships between child and parent model - django

I am busy with a project where I have two models and I'm not sure how to do as I am fairly new to programming.
what I would like to do is have a template that
renders all the instances of first model
and then on the same template I want to have all the sales associated with each of those instances as well as the instances with different states(for example if an instance of first model is linked to 2 sales that has a state of "Confirmed" it should say 2 next to that instance name.)
class QAAgent(models.Model):
user=models.OneToOneField(User,on_delete=models.SET_NULL,null=True)
Qa_name = models.CharField(max_length=15)
def __str__(self):
return self.user.username
States = (('Pending',"Pending"),("Confirmed","Confirmed"),("Requested","Requested),("Cancelled","Cancelled"),("Not interested","Not interested"))
class Sale(models.Model):
QA = models.ForeignKey(QAAgent,on_delete=models.SET_NULL,blank=True,
null=True,related_name="sale")
State = models.CharField(choices=States,default="Pending",max_length=15)
Date_created = models.DateTimeField(auto_now_add=True)
Date_edited = models.DateTimeField(auto_now=True)
def__str__(self):
return self.client_name + " " + self.client_surname
I am not sure how to reply to answers but thanks guys that worked perfectly!

To list all your entries of the first model including it's related items you can do the following. Just be sure to pass the correct queryset to the view context.
{% for entry in qa_agent %}
{{ entry.qa_name}}
{{ entry.user.name }}
{% for sale in entry.qa_sale.all %}
{{sale.category.state }}
{% endfor %}
{% endfor %}
For your second request just create a method in your model class which filters for the desired status. I've called it get_confirmed here.
class QAAgent(models.Model):
user = models.OneToOneField(User, on_delete=models.SET_NULL, null=True)
qa_name = models.CharField(max_length=15) # You had Qa_name before
def get_confirmed(self):
return self.qa_sale.filter(state="Pending") # just write a method like this one for each of the other statuses.
def __str__(self):
return self.user.username
And then modify your template code so it uses this method to display the confirmed ones.
{% for entry in qa_agent %}
{{ entry.qa_name}}
{{ entry.user.name }}
{{ entry.get_confirmed.count }} # <-- like this
{% endfor %}
Just a quick side note about python code styling.
For variables and function names use snake_case. for classes use CamelCase.
So for your model properties it's best to only use lower case letters and separate words using an underscore. For classes start with an uppercase letter and separate words by using an uppercase letter for the first letter of the new word. This is how it should look like:
states = (
("Pending", "Pending"),
("Confirmed", "Confirmed"),
("Requested", "Requested"),
("Cancelled", "Cancelled"),
("Not interested", "Not interested"),
)
class Sale(models.Model):
qa = models.ForeignKey(QAAgent, on_delete=models.SET_NULL, blank=True, null=True, related_name="qa_sale")
state = models.CharField(choices=States, default="Pending", max_length=15)
date_created = models.DateTimeField(auto_now_add=True)
date_edited = models.DateTimeField(auto_now=True)
def __str__(self):
return f"{self.client_name} {self.client_surname}" # If you concatenate strings use an fstring like I did here. It's more readable

Related

DJANGO Supplement to the data list via an intermediate table

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.

Perform query in django against another table, but its not finding the table

I am trying to perform a query via a join.
My models.py looks like:
class TSFH(models.Model):
sB = models.DateTimeField(null=True)
sE = models.DateTimeField(null=True)
FKToUser = models.ForeignKey(User,default=None, on_delete=models.PROTECT)
class Tld(models.Model):
dNm = models.CharField(verbose_name="",max_length=40,unique=True)
FKToUser = models.ForeignKey('auth.user', default=None, on_delete=models.PROTECT)
class TSF(models.Model):
FKToT = models.ForeignKey('T', on_delete=models.PROTECT)
FKToTSFH = models.ForeignKey('TSFH', on_delete=models.PROTECT, null=True, blank=True)
views.py
enteredd = request.GET.get('d', '')
query=TSFH.objects.filter(FKToT__dNm=enteredd,FKToUser=request.user,sE__isnull=False)
return render(request, 'view.html', {'q':q})
templates/file.html
{% if q %}
{% for res in q %}
<li><span>{{ res.sE }}</span></li>
{% endfor %}
{% else %}
{% endif %}
When I view this all it complains about:
Cannot resolve keyword 'FKToT' into field
In plain sql I'm looking to perform the following just with dNm search directly from the enteredd instead of a hardcoded value of 123.145.23.1 as shown below:
SELECT sE
FROM tsfh, tsf, t
where
tsfh.id=tsf.FKTotsfh_id
and
t.id=tsf.FKToT_id
and
tsfh.sE is not null
and
t.dNm='123.145.23.1';
How can I keep these existing checks in the query but include this FKToT__dNm=enteredd somehow?
Thank you very much.
Do a little modification at this line:
query=TSFH.objects.filter(TST__FKToT__dNm=......)
If you put this meta into your class, it will specifically name the table.
class Meta:
db_table = '<your_table_name>'
You can read more here.

how to make URL pattern accept variable arguments in Django?

my title may not be very clear.
The problem I am facing now is:
my view passes arbitrary keyword arguments to another view.
This view handles with what info(keyword arguments it gets)
This is the example of search feature I am implementing.
Each user will have a list of Saved Searches. when they click on any one of the items in Search list, they will be directed to a different view that process the information it receives
In the Searches model, I have defined a get_absolute_url method that constructs the URL pattern of each of these search (based on models in the field).
my model:
class Searches(models.Model):
SELLER_CHOICES=(('OWNER','owner'),
('DEALER','dealer'),
('BOTH','both'), )
#search_id = models.IntegerField(primary_key=True)
user = models.ForeignKey(User)
make = models.CharField(max_length=100, blank=True)
model = models.CharField(max_length=100, blank=True)
keywords = models.CharField(max_length=100, blank=True)
max_price = models.IntegerField(blank=True, null=True)
min_price = models.IntegerField(blank=True, null=True)
max_year = models.IntegerField(blank=True, null=True)
min_year = models.IntegerField(blank=True, null=True)
pic_only = models.NullBooleanField()
search_title_only = models.NullBooleanField()
owner_dealer_all = models.CharField(max_length=10,choices=SELLER_CHOICES,verbose_name='owner/dealer')
class Meta:
#managed = False
db_table = 'Searches'
verbose_name_plural = "Searches"
def __unicode__(self):
return "%s %s %s-%s" %(self.make,self.model,self.max_year,self.min_year)
def get_absolute_url(self):
return reverse('postings.views.detail',args=[model_to_dict(self.object)])
view:
class SearchListView(ListView):
model=Searches
template:
{% extends "base.html" %}
{% block content %}
{% for obj in object_list %}
<p>{{ obj }}</p>
{% endfor %}
{% endblock %}
you can see from the image, when I click the your searches, I get error;
Reverse for 'postings.views.detail' with arguments '({'owner_dealer_all': u'DEALER', 'pic_only': True, 'make': u'toyota', u'id': 3, 'min_year': 1990, 'min_price': 4000, 'user': 1, 'keywords': u'hybrid', 'search_title_only': True, 'model': u'prius', 'max_price': 20000, 'max_year': 2012},)' and keyword arguments '{}' not found. 0 pattern(s) tried: []
Basically, I dont know how to handle this in the URL pattern.
OR
IF THIS IS A BAD DESIGN, PLEASE PLEASE SUGGEST A SOLUTION
If you really need to have all the variables in the URL, then you need to pass the variables as params of a GET query, so your URL will look like this:
http://example.com/search?owner_dealer_all=DEALER&pic_only=True&make=toyota&id=3&min_year=1990&min_price=4000&user=1&keywords=hybrid&search_title_only=True&model=prius&max_price=20000&max_year=2012
But, I would strongly suggest that you keep that URL as separate from the Search.get_absolute_url result, as the latter is supposed to point to the actual model. So have that function return something like:
http://example.com/search/23465
where the number is the ID for your Search model instance. In that view, get all the saved details and make a search on them. If you actually don't need the data in your URL, simply opt for the latter version only.

How should I approach creating an iterable in a Django view that will allow me to group items by a foreign key (or lack thereof) in a template?

I have constructed a view that subclasses Django's ListView. Currently it works fine. It provides a list of objects from my Artwork class that I can iterate over in my template.
However, I've now included a (not required) foreign key (series) and I would like to group the Artwork objects by series when I display them. I would also like to order them by date while interspersing any Artwork objects with series=None amongst them (according to the date also).
Like this:
artwork(12/2013) (series=None)
artwork(12/2013) artwork(12/2013) artwork(11/2013) (series='series1' 11/2013)
artwork(10/2013) (series=None)
artwork(12/2013) artwork(12/2013) artwork(11/2013) (series='series2' 09/2013)
My models look like this:
class Series(models.Model):
title = models.CharField(max_length=200, unique=True)
date = models.DateTimeField(default=timezone.now(), editable=False)
def __unicode__(self):
return self.title
class Artwork(models.Model):
title = models.CharField(max_length=200, unique=True)
series = models.ForeignKey(Series, blank=True, null=True, on_delete=models.SET_NULL)
date = models.DateTimeField(default=timezone.now(), editable=False)
image = models.ImageField(upload_to='artwork_images/%Y/%m/')
def __unicode__(self):
return self.title
I'm guessing I could somehow accomplish what I want if my view were to provide the template with something like a nested list. Is this possible with the ListView? Or should I maybe be constructing a function based view?
Apologies if this seems like a naive question. I'm new to the Django/Python world (and Stackoverflow!). Any advice or suggestions would be very much appreciated.
I would just use a normal function based view for this, but if you want you can use ListView's get_context_data() method to return arbitrary data.
Create a key function for sorting:
def key_function(artwork):
if artwork.series is None:
return (artwork.date, None)
else:
return (artwork.series.date, artwork.series)
The function uses serie's date if avialiable and artwork's date otherwise. Series id is a second part of the key (when available), so artworks belonging to the same series are kept next to each other, even if there are more than one serie with the same date.
Sort your data using the key:
def your_view(request):
artwork = Artwork.objects.select_related()
artwork_list_sorted = sorted(artwork, key=key_function)
return render(request, "your_app/artwork_list.html", {'artwork_list_sorted': artwork_list_sorted})
or using ListView:
def get_context_data(self, **kwargs):
context = super(ArticleListView, self).get_context_data(**kwargs)
queryset = self.get_queryset()
context['artwork_list_sorted'] = sorted(queryset, key=key_function)
return context
Use the regroup template tag to group your data:
{% regroup artwork_list_sorted by series as artwork_grouped %}
{% for artwork in artwork_grouped %}
{% for item in artwork.list %}
{{ item.title }}({{ item.date }})
{% endfor %}
<strong>(series={{ artwork.grouper }})</strong>
{% endfor %}
or alternatively just iterate through the original sorted list, displaying series when needed - using the ifchanged template tag:
{% for artwork in artwork_list_sorted %}
{{ artwork.title}}({{ artwork.date }})
{% ifchanged %}<strong>(series={{ artwork.series.title }})</strong>{% endifchanged %}
{% endfor %}

subquery in django

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.