Django output 1 field from a model within another table - django

I have 2 models -
class InsName(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=30, verbose_name = "Insurer/Broker")
alias = models.TextField(max_length=80, blank=True)
def __str__(self):
return f'{self.name}, {self.alias}'
def get_absolute_url(self):
return reverse('insurer-detail', args=[str(self.id)])
class Development(models.Model):
id = models.AutoField(primary_key=True)
logno = models.CharField(validators=[RegexValidator(regex='^(SCTASK|CDLI)[0-9]{7}', message='Please enter a valid log number', code='nomatch')], max_length=13)
insurer = models.ForeignKey(InsName, on_delete=models.SET_NULL, null=True, blank=False, verbose_name="Client")
policy = models.ManyToManyField(Policy, blank=True)
on my template I am outputting a list of Developments but where insurer is output I just want the name part to output. I need to retain the alias as it is used in other templates that also calls in InsName.
I thought I could use a substring before comma method in the template but I cant see that such a thing exists. Is this possible? If not any tips on how I can achieve this is greatly appreciated!

Maybe you can do it like this using F (apart from comment of #dirkgroten):
queryset = Development.objects.all().annotate(insurer_name=F('insurer__name'))
And use it in template:
{% for item in queryset %}
{{ item.insurer_name }}
{% endfor %}

Related

How can I display something from data base after multiple criteria is satisfied in django

Here i have a Model Recommenders:
class Recommenders(models.Model):
objects = None
Subject = models.ForeignKey(SendApproval, on_delete=models.CASCADE, null=True)
Recommender = models.CharField(max_length=20, null=True)
Status = models.CharField(null=True, max_length=8, default="Pending")
Time = models.DateTimeField(auto_now_add=True)
And another model Approvers:
class Approvers(models.Model):
objects = None
Subject = models.ForeignKey(SendApproval, on_delete=models.CASCADE, null=True)
Approver = models.CharField(max_length=20, null=True)
Status = models.CharField(null=True, max_length=8, default="Pending")
Time = models.DateTimeField(auto_now_add=True)
And my SendApproval model as:
class SendApproval(models.Model):
Subject = models.CharField(max_length=256)
Date = models.DateField(null=True)
Attachment = models.FileField(upload_to=get_file_path)
SentBy = models.CharField(null=True, max_length=100)
Status = models.CharField(null= True, max_length=8, default="Pending")
Now my problem is that I have to display the Subject and Attachment from SendApproval table only when all the recommender's Status in Recommenders table related to that subject is "Approved"
Don't know how can I know that...Thanks in advance...
Actually not have any Idea about the solution but the best answer will be appreciated...By the way, I am new to StackOverflow...So please let me know if there is some ambiguity in my question.
Offer two ways.
1.Here the Recommenders model is filtered by the Status='Approved' field. By the Subject field, which is associated with the primary model, I get access to the SendApproval fields.
Replace bboard with the name of the folder where your templates are placed.
I have this: templates/bboard which are in the application folder.
views.py
def info(request):
recomm = Recommenders.objects.filter(Status='Approved')
return render(request, 'bboard/templ.html', {'context': recomm})
urls.py
urlpatterns = [
path('info/', info, name='info'),
]
templates
{% for a in context %}
<p>{{ 'Subject' }} : {{ a.Subject.Subject }} {{ 'Attachment' }} : Link
{{ 'id SendApproval' }} : {{ a.Subject.id }}</p>
{% endfor %}
2.You can also filter by primary model by fields from a secondary model that is linked to the primary. Here, I passed in the filter the name of the model in lower case and the search field.
In the primary model, by default, a property is created to refer to the secondary model, this is the name of the model in lower case and the _set prefix. In the outer loop, all received rows of the primary model are sorted out, and in the inner loop, through recommenders_set.all(), we get the Status. As you can see it is Approved.
sa = SendApproval.objects.filter(recommenders__Status='Approved')
print(sa)
for i in sa:
for k in i.recommenders_set.all():
print('status', k.Status)

Django view for link table

I have three tables in my model:
class Recipe(models.Model):
title = models.CharField(max_length=200)
excerpt = models.TextField(null=True)
category = models.CharField(max_length=10, default='FoodGroup')
cookTime = models.CharField(max_length=20, default='15Min')
prepTime = models.CharField(max_length=20, default='5Min')
process = models.TextField(null=True)
slug = models.SlugField(max_length=100, unique=True)
updated = models.DateTimeField(auto_now=True)
published = models.DateTimeField(default=timezone.now)
def get_absolute_url(self):
return reverse('recipe:single', args=[self.slug])
class Meta:
ordering = ['-published']
def __str__(self):
return self.title
class Ingredient(models.Model):
IngName = models.CharField(max_length=30)
IngType = models.CharField(max_length=20, null=True)
def __str__(self):
return self.IngName
class RecipeIngredients(models.Model):
ingredient = models.ForeignKey(Ingredient, on_delete=models.CASCADE)
recipe = models.ForeignKey(Core, on_delete=models.CASCADE)
And the view looks like this:
class RecipeView(ListView):
model = RecipeIngredients
template_name = 'core/recipes.html'
context_object_name = 'recipe'
I'm trying to then view this data using a for loop:
{% for recipe in recipe %}
{{ recipe.title }}
{% endfor%}
but nothing gets pulled using this method, I've tried looking at the docs but I'm unsure I'm doing this in the best way? I had a look at the many-to-many section of the Docs but I thought a link-table may be more appropriate for my scenario.
edit for anyone else having similar issues:
Using Django's ManyToMany model function ended up being more appropriate:
https://docs.djangoproject.com/en/3.0/topics/db/examples/many_to_many/
Answer was correct in saying object_name was poorly chosen.
The name of your iterable must be different from context_object_name.
Try different name like:
{% for diff_name in recipe %}
{{ diff_name.title }}
{% endfor%}

How to query in Django with best efficiency?

I recently found that too much SQL query optimization issue. django-debug-tool reported hundreds of similar and duplicate queries. So, I'm trying to figure out the best efficiency of Django ORM to avoid unnecessary Queryset evaluation.
As you see the below Store model, a Store model has many Foreign key and ManyToManyFields. Due to that structure, there are many code snippets doing the blow on HTML template files such as store.image_set.all or store.top_keywords.all. Everything starts with store. In each store detail page, I simply pass a cached store object with prefetch_related or select_related. Is this a bad approach? Should I cache and prefetch_related or select_related each Foreign key or ManyToManyField separately on views.py?
HTML templates
{% for img in store.image_set.all %}
{{ img }}
{% endfor %}
{% for top_keyword in store.top_keywords.all %}
{{ top_keyword }}
{% endfor %}
{% for sub_keyword in store.sub_keywords.all %}
{{ sub_keyword }}
{% endfor %}
views.py
class StoreDetailView(View):
def get(self, request, *args, **kwargs):
cache_name_store = 'store-{0}'.format(store_domainKey)
store = cache.get(cache_name_store, None)
if not store:
# query = get_object_or_404(Store, domainKey=store_domainKey)
query = Store.objects.all().prefetch_related('image_set').get(domainKey=store_domainKey)
cache.set(cache_name_store, query)
store = cache.get(cache_name_store)
context = {
'store': store,
}
return render(request, template, context)
models.py
class Store(TimeStampedModel):
categories = models.ManyToManyField(Category, blank=True)
price_range = models.ManyToManyField(Price, blank=True)
businessName = models.CharField(unique=True, max_length=40,
verbose_name='Business Name')
origin = models.ForeignKey(Origin, null=True, on_delete=models.CASCADE, blank=True)
ship_to = models.ManyToManyField(ShipTo, blank=True)
top_keywords = models.ManyToManyField(Keyword, blank=True, related_name='store_top_keywords')
sub_keywords = models.ManyToManyField(SubKeyword, blank=True, related_name='store_sub_keywords')
sponsored_stores = models.ManyToManyField(
'self', through='Sponsorship', symmetrical=False, related_name='sponsored_store_of_store')
similar_stores = models.ManyToManyField(
'self', through='Similarity', symmetrical=False, related_name='similar_store_of_store')
shortDesc = models.TextField(blank=True, verbose_name='Short Description')
longDesc = models.TextField(blank=True, verbose_name='Long Description')
returnPol = models.TextField(verbose_name='Return Policy', blank=True)
returnUrl = models.CharField(max_length=255, null=True, blank=True, verbose_name='Return Policy URL')
likes = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True, editable=False)
created_by = models.ForeignKey(settings.AUTH_USER_MODEL, editable=False, on_delete=models.CASCADE,
related_name='stores_of_created_by', null=True, blank=True)
updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, editable=False, on_delete=models.CASCADE,
related_name='stores_of_updated_by', null=True, blank=True)
I really wouldn't advise custom caching/performance optimisation, unless it's a very last resort. Django has great docs on querysets and optimisation - if you follow those, it should be rare for you to experience major performance issues that require custom workarounds.
I think the issue here is that you're printing your objects in a template and hence calling their str() method. There's nothing wrong with this, but I'd check what variables you're using in your str() methods. I suspect you're referencing other models? I.e. the str() method in your image model (or whatever) is doing something like image.field.other_field. In this case, your query should look like:
queryset = Store.objects.prefetch_related('image_set__field')
Your final queryset may look like:
queryset = Store.objects.prefetch_related('image_set__field1', 'image_set__field2', 'top_keywords__field3', ...)
Note that you can still pass this into get_object_or_404 like so:
get_object_or_404(queryset, pk=<your_stores_id>)
Hope this helps.

Accessing last row from table C that has FK from table B, which is in one-to-one relationship with A

Let's say we have 3 models:
class A(models.model):
name = models.CharField(max_length=30, unique=True)
class B(models.model):
name = models.CharField(max_length=30, unique=True)
a = models.OneToOneField(A, on_delete=models.CASCADE, primary_key=True)
class C(models.model):
name = models.CharField(max_length=30, unique=True)
b = models.ForeingKey(B, on_delete=models.CASCADE)
transaction_count = models.IntegerField(default=0)
and one view:
class AListView(generic.ListView):
model = A
In that view (template) I need to show: name of A, name of B and the last row (ordered by date) of "transactioncount" for each repository from b.
In my template I iterate over items in A and show them in following way:
{% for a in A %}
<tr>
<td>{{a.name}}</td>
<td>{{a.b.name}}</td>
<td>{{??? Don't know what to put here, to show the last row. I tried: a.b.c|last}}</td>
</tr>
{% endfor %}
I tried to build custom tags and use template functions like, but that unfortunately doesn't work:
{% with a.b.c_set.all|last as val %}
<td>val</td>
{% endwith}
Among my other tries would be to build a new queryset, but then I don't know how to assign items from model A to that queryset. I tried:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({
'A': A.objects.all(),
'c_data': C.objects.order_by('B', '-date').distinct(
'B')
})
)
What would be the best "pythonic" way to do this?
Thanks
First, of all, don't forget to set the back relation name like:
b = models.ForeingKey(B, on_delete=models.CASCADE, related_name='all_c')
Then:
class B(models.model):
name = models.CharField(max_length=30, unique=True)
a = models.OneToOneField(A, on_delete=models.CASCADE, primary_key=True)
#property
def last_c(self):
if self.all_c.exists():
return self.all_c.order_by('-date').last()
In your jinja template just write:
a.b.last_c
Have fun!

View returns 404 when including a queryset despite the object being valid

I'm using the following URL pattern to pass a primary key for a model to the view class detailed below it. The primary key of the related model, "Host", is either a valid IP address or FQDN.
URL Pattern:
ValidIpAddressRegex = "(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])"
ValidHostnameRegex = "(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])"
url(r"^host/(?P<pk>({}|{}))/$".format(ValidIpAddressRegex, ValidHostnameRegex),
views.HostView.as_view(),
name="host")
View class:
class HostView(generic.DetailView):
model = Host
template_name = 'report_viewer/host.html'
context_object_name = 'related_findings'
def get_queryset(self):
"""Return all related findings"""
host = get_object_or_404(Host, name=self.kwargs["pk"])
return host.get_findings() # A function on the model to return related records from another model
Relevant template:
<ul>
{% for finding in related_findings %}
<li><strong>{{ finding.plugin_id }}</strong> {{ finding.plugin_name }}</li>
{% empty %}
<li>No findings!</li>
{% endfor %}
</ul>
Host model:
class Host(models.Model):
name = models.CharField(max_length=255, primary_key=True)
os_general = models.CharField(max_length=255, blank=True, verbose_name="Generic OS Identifier")
os_specific = models.CharField(max_length=255, blank=True, verbose_name="Reported OS Name")
ssh_fingerprint = models.CharField(max_length=255, blank=True, verbose_name="SSH Fingerprint")
mac_address = models.CharField(max_length=17, blank=True, verbose_name="MAC Address")
ip_address = models.GenericIPAddressField(blank=True, null=True, verbose_name="IP Address")
fqdn = models.CharField(max_length=255, blank=True, verbose_name="Fully Qualified Domain Name")
netbios_name = models.CharField(max_length=255, blank=True, verbose_name="NetBIOS Name")
def __str__(self):
return self.name
def get_findings(self):
findings = Finding.objects.filter(targets=self)
return findings
class Meta:
ordering = ['name']
The result of a valid URL is "No finding found matching the query", raised by views.HostView. The view below works successfully, invoking the {% empty %} condition, which suggests that I'm either constructing the get_object_or_404 request incorrectly, or perhaps the "pk" variable is being mangled and is not a valid key.
I'd appreciate any thoughts on a solution. Or indeed an alternative method to achieve the same outcome.
Turns out the solution was a great deal simpler than expected. I was attempting to use the get_queryset function with the generic DetailView, when it's meant to be used with ListView.
I rewrote the model to use the get_context_data function instead.
class HostView(generic.DetailView):
model = Host
template_name = 'report_viewer/host.html'
def get_context_data(self, **kwargs):
"""Return all related findings"""
context = super(HostView, self).get_context_data(**kwargs)
context['host'] = get_object_or_404(Host, name=self.kwargs["pk"])
context['findings'] = context['host'].get_findings()
return context
Which then allows me to access the relevant Host model and all its related Finding models in the template.