Displaying many-to-many subsets with different names? in Django template - django

I plan to get rid of the related name the next time I rebuild the database...the models I am using are more of test-models. So, with Class Creator and the writer, cover_artist, etc., how would I go about displaying the issues that Creator has created (once I get rid of the related name, unless there's a way to go around it)?
class Creator(models.Model):
name = models.CharField(max_length=256)
desc = models.TextField("description", blank=True, null=True)
#type = writer, artist, editor, letterer
slug = models.SlugField(blank=True, null=True)
def __unicode__(self):
return self.name
class Meta:
ordering = ['name']
def get_absolute_url(self):
return "/comics2/creators/%s" % self.slug
class Issue(models.Model):
....
writer = models.ManyToManyField(Creator, related_name="written by", help_text="Use cmd/ctrl + click to select multiple names. The same applies to the rest of the creators below.", blank=True, null=True)
artist = models.ManyToManyField(Creator, related_name="drawn by", blank=True, null=True)
cover_artist = models.ManyToManyField(Creator, related_name="cover drawn by", blank=True, null=True)
colorist = models.ManyToManyField(Creator, related_name="colored by", blank=True, null=True)
inker = models.ManyToManyField(Creator, related_name="inked by", blank=True, null=True)
penciller = models.ManyToManyField(Creator, related_name="pencilled by", blank=True, null=True)
letterer = models.ManyToManyField(Creator, related_name="lettered by", blank=True, null=True)
editor = models.ManyToManyField(Creator, related_name="edited by", blank=True, null=True)
arc = models.ManyToManyField(Arc, related_name="related story arc", blank=True, null=True)
...
def __unicode__(self):
return u'%s %s' % (self.title, self.number)
def get_absolute_url(self):
return "/comics2/issues/%s" % self.slug
class Meta:
ordering = ['title', 'number']
def get_stars(self):
star_rating = self.rating * 16
return star_rating
....
{% for issue in creator.____?__.all %}
<ul>
<li>{{ issue }}</li>
</ul>
{% endfor %}
Is not going to work.
Thanks.

I would suggest changing your models.py to have Issue have a Many to Many relationship to Creator through another table, e.g. Role. See docs here.
class Creator(models.Model):
name = models.CharField(max_length=256)
desc = models.TextField("description", blank=True, null=True)
slug = models.SlugField(blank=True, null=True)
def __unicode__(self):
return self.name
class Meta:
ordering = ['name']
def get_absolute_url(self):
return "/comics2/creators/%s" % self.slug
class Issue(models.Model):
creators = models.ManyToManyField(Creator, through='Role')
arc = models.ManyToManyField(Arc, related_name="related story arc", blank=True, null=True)
...
def __unicode__(self):
return u'%s %s' % (self.title, self.number)
def get_absolute_url(self):
return "/comics2/issues/%s" % self.slug
class Meta:
ordering = ['title', 'number']
def get_stars(self):
star_rating = self.rating * 16
return star_rating
class Role(models.Model):
issue = models.ForeignKey(Issue)
creator = models.ForeignKey(Creator)
role = models.CharField()
...
edit:
in your issues view, you would get the different roles and pass it to your template (for example):
def issue_detail(request, issue_slug=None):
issue = get_object_or_404(Issue, slug=creator_slug)
writers = issue.creators.filter(role__role='writer')
cover_artists = issue.creators.filter(role__role='cover artist')
...
context = { 'issue': issue,
'writers': writers,
'cover_artists': cover_artists,
...
}
return render_to_response('issue_detail.html', context,
context_instance=RequestContext(request))
template:
<label>Writers</label>
{% for writer in writers %}
{{ writer }}
{% endfor %}
<label>Cover Artists</label>
{% for cover_artist in cover_artists %}
{{ cover_artist }}
{% endfor %}
creator view:
def creator_detail(request, issue_slug=None):
creator = get_object_or_404(Creator, slug=issue_slug)
issues_as_writer = creator.issue_set.filter(role__role='writer')
issues_as_cover_artists = creator.issue_set.filter(role__role='cover artist')
#all issues related to this creator
all_issues = creator.issue_set.all()
...
context = { 'creator': creator,
'issues_as_writer': issues_as_writer,
'issues_as_cover_artist': issues_as_cover_artist,
...
'all_issues': all_issues,
}
return render_to_response('creator_detail.html', context,
context_instance=RequestContext(request))

Related

Django how to generate a filtered queryset

I have got 2 models. Asset and Asset_Type. In my asset_type detail view i would like to list all assets of that asset type. I think I have to use models.Asset.queryset().filter() but i can't get it to work.
On my template I would like to loop though the list with a 'for' (example: object in list) and print the values like this {{ object.name }}
models.py
class Asset(models.Model):
# Relationships
room = models.ForeignKey("asset_app.Room", on_delete=models.SET_NULL, blank=True, null=True)
model_hardware = models.ForeignKey("asset_app.Model_hardware", on_delete=models.SET_NULL, blank=True, null=True)
# Fields
name = models.CharField(max_length=30)
serial = models.CharField(max_length=30, unique=True, blank=True, null=True, default=None)
mac_address = models.CharField(max_length=30, null=True, blank=True)
purchased_date = models.DateField(null=True, blank=True)
may_be_loaned = models.BooleanField(default=False, blank=True, null=True)
notes = models.TextField(max_length=448, null=True, blank=True)
ip = models.CharField(max_length=90, null=True, blank=True)
created = models.DateTimeField(auto_now_add=True, editable=False)
last_updated = models.DateTimeField(auto_now=True, editable=False)
class Meta:
ordering = ["name"]
def __str__(self):
return str(self.name)
def get_absolute_url(self):
return reverse("asset_app_asset_detail", args=(self.pk,))
def get_update_url(self):
return reverse("asset_app_asset_update", args=(self.pk,))
class Asset_type(models.Model):
# Fields
name = models.CharField(max_length=30)
last_updated = models.DateTimeField(auto_now=True, editable=False)
created = models.DateTimeField(auto_now_add=True, editable=False)
notes = models.TextField(max_length=448, null=True, blank=True)
class Meta:
ordering = ["name"]
def __str__(self):
return str(self.name)
def get_absolute_url(self):
return reverse("asset_app_asset_type_detail", args=(self.pk,))
def get_update_url(self):
return reverse("asset_app_asset_type_update", args=(self.pk,))
class Model_hardware(models.Model):
# Relationships
asset_type = models.ForeignKey("asset_app.Asset_type", on_delete=models.SET_NULL, blank=True, null=True)
brand = models.ForeignKey("asset_app.Brand", on_delete=models.SET_NULL, blank=True, null=True)
# Fields
name = models.CharField(max_length=30)
notes = models.TextField(max_length=448, null=True, blank=True)
created = models.DateTimeField(auto_now_add=True, editable=False)
last_updated = models.DateTimeField(auto_now=True, editable=False)
class Meta:
ordering = ["name"]
def __str__(self):
return str(self.name) + " :: " + str(self.brand.name) + " :: " + self.asset_type.name
def get_absolute_url(self):
return reverse("asset_app_model_hardware_detail", args=(self.pk,))
def get_update_url(self):
return reverse("asset_app_model_hardware_update", args=(self.pk,))
views.py
class Asset_typeDetailView(generic.DetailView):
model = models.Asset_type
form_class = forms.Asset_typeForm
You can simply iterate over the related objects in the template by using the default related name which is the model name in lowercase with _set appended. So asset_type.model_hardware_set.all() will give you all Model_hardware instances related to Asset_type and similarly for model_hardware.asset_set.all():
{% for model_hardware object.model_hardware_set.all %}
{% for asset in model_hardware.asset_set.all %}
{{ asset.name }}
{% endfor %}
{% endfor %}
But this can become slow, since we run into the N + 1 problem that is for each model hardware we will be making queries to get it's assets. We can use prefetch_related_objects on your model instance to prefetch all the related objects (in fewer queries) this and make it faster:
from django.db.models import prefetch_related_objects
from django.views.generic import DetailView
class YourDetailView(DetailView):
model = Asset_type
template_name = '<your_template_name>.html'
def get_object(self, queryset=None):
obj = super().get_object(queryset=queryset)
prefetch_related_objects([obj], 'model_hardware__asset')
return obj
Note: Class names in python should ideally be in PascalCase not Some_case (Don't think there is any such convention as you make
here), hence ModelHardware instead of Model_hardware and
AssetType instead of Asset_type would be better names.

Django show extra content in class based view with ForeignKey models

I'm trying to add extra content to Djangos Class-based view to the template
I have some models like this
class District(models.Model):
district = models.CharField(max_length=255, null=False, unique=False, blank=True)
def __str__(self):
return self.district
class Street(models.Model):
street_name = models.CharField(max_length=255, null=False, unique=False, blank=True)
district = models.ForeignKey(District, verbose_name=_('district'), on_delete=models.CASCADE, null=True, blank=True)
zone = models.IntegerField(blank=True, null=True)
def __str__(self):
return self.street_name
class Article(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, related_name="author", on_delete=models.SET_NULL)
timestamp = models.DateTimeField(auto_now_add=True)
status = models.CharField(max_length=1, choices=STATUS, default=CREATED)
comment = models.CharField(max_length=255, null=True, unique=False, blank=True)
name = models.CharField(max_length=255, null=True, unique=False)
street = models.ForeignKey(Street, verbose_name=_('street'), on_delete=models.CASCADE, null=True, blank=True)
class ArticlesListView(LoginRequiredMixin, PermissionRequiredMixin,ListView):
model = Article
paginate_by = 50
context_object_name = "articles"
permission_required = 'is_staff'
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['Filter_name'] = Article.objects.order_by().values('name').distinct()
context['Filter_user'] = Article.objects.order_by().values('user').distinct()
return context
def get_queryset(self, **kwargs):
return Article.objects.all()
And late in the template
{% for f in Filter_name %}
<ul>
<li>{{f.name}}</li>
</ul>
{% endfor %}
How can I display a list of the district names and a list of the author names in the template with ForeignKey?
U can try something like that
{% for item in model_1.foreign_model_set.all %}
<h1>{{ item }}</h1>
{% endfor %}

Django - Annotation not showing in template

I'm trying to get annotations showing in my templates. I have two models (model1 and model2) and I want to show the number of model2's related to model1.
Here is my views.py:
def model2_count(request, pk):
model2count = models.Model1.objects.filter(pk=model1.pk).annotate(title_count=Count(‘model2__title'))
return render(request, 'model1/_model1.html', {‘m2c’: model2count})
Here is the template (model1/_model1.html):
I tried this:
{% for object in m2c %}</h3>
{{ object.title }}
{{ object.title_count }}
{% endfor %}
And tried this:
{% if m2c.title_count %}
{{ m2c.title_count }}
{% endif %}
I've been pulling my hair out over this for a couple days and can't figure it out. The following has been largely unhelpful:
Django - Annotating Weighted AVG by Group
Django: Annotation not working?
Django templates are not showing values of annotations
Django annotated value in template
What's frustrating is that I can't even say why applying these solutions hasn't worked.
Any input is appreciated.
Also, here are my models with all the BS taken out.
class Publication(models.Model):
title = models.CharField(max_length=150, unique=False, blank=False)
contributors_note = models.TextField(max_length=300, blank=False)
website = models.URLField()
publisher = models.CharField(max_length=250, unique=False)
publication_date = models.DateField(default=datetime.date.today)
slug = models.SlugField(allow_unicode=True, unique=False)
content_type = models.CharField(max_length=100, unique=False)# In this field user's define the type of content (blog, newspaper article, publication etc)
research_type = models.CharField(max_length=100, unique=False)# In this field user's define whether the research is based on primary or secondary research
user = models.ForeignKey(Current_user, related_name="publication")
created_at = models.DateTimeField(auto_now_add=True)
last_updated = models.DateTimeField(auto_now=True)
category = models.ForeignKey(Category, related_name="publication",null=True, blank=False)
comment = models.TextField()
def __str__(self):
return self.title
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super().save(*args, **kwargs)
def get_absolute_url(self):
return reverse(
"publication:single",
kwargs={
"username": self.user.username,
"pk": self.pk
}
)
class Meta:
ordering = ["-created_at"]
class Assessment(models.Model):
title = models.CharField(max_length=150, unique=False, blank=False)
publication = models.ForeignKey('publication.Publication', on_delete=models.CASCADE, related_name='assessment')
analyst = models.ForeignKey(Current_user, null=True, blank=True, related_name="assessment")
created_at = models.DateTimeField(auto_now_add=True)
approved_comment = models.BooleanField(default=False)
key_finding1 = models.TextField(max_length=300)
key_finding2 = models.TextField(max_length=300)
key_finding3 = models.TextField(max_length=300)
ratings_range = (
('1', 'Very Weak'),
('2', 'Weak'),
('3', 'Moderate'),
('4', 'Strong'),
('5', 'Very Strong'),
)
content_rating_1 = models.CharField(max_length=1, choices=ratings_range)
content_rating_1_comment = models.TextField(max_length=300)
content_rating_2 = models.CharField(max_length=1, choices=ratings_range)
content_rating_2_comment = models.TextField(max_length=300)
content_rating_3 = models.CharField(max_length=1, choices=ratings_range)
content_rating_3_comment = models.TextField(max_length=300)
content_rating_4 = models.CharField(max_length=1, choices=ratings_range)
content_rating_4_comment = models.TextField(max_length=300)
content_rating_5 = models.CharField(max_length=1, choices=ratings_range)
content_rating_5_comment = models.TextField(max_length=300)
source_rating_1 = models.CharField(max_length=1, choices=ratings_range)
source_rating_1_comment = models.TextField(max_length=300)
source_rating_2 = models.CharField(max_length=1, choices=ratings_range)
source_rating_2_comment = models.TextField(max_length=300)
source_rating_3 = models.CharField(max_length=1, choices=ratings_range)
source_rating_3_comment = models.TextField(max_length=300)
source_rating_4 = models.CharField(max_length=1, choices=ratings_range)
source_rating_4_comment = models.TextField(max_length=300)
source_rating_5 = models.CharField(max_length=1, choices=ratings_range)
source_rating_5_comment = models.TextField(max_length=300)
def approve(self):
self.approved_comment = True
self.save()
def __str__(self):
return self.title
class Meta:
ordering = ["-created_at"]
Bad mistake on my part. The solution's given above worked. Here is my final code:
views.py
class PublicationDetail(SelectRelatedMixin, generic.DetailView):
model = models.Publication
select_related = ("category", "user")
def get_queryset(self):
queryset = super().get_queryset()
return queryset.filter(user__username__iexact=self.kwargs.get("username")).annotate(assessment_count=Count('assessment'))
Fellow noobs that follow:
- It's easier for the people that want to help you (and faster for you) if you just post your original code instead of trying to get smart. Don't be embarrassed. I would've saved us all time if I had just done that.
First aggregations are done only using the field name i.e model2 not model2__title
Next getting counts of annotated columns recommended to use .values or values_list but not required.
https://docs.djangoproject.com/es/2.1/topics/db/aggregation/#cheat-sheet
model1 = Model1.objects.get(pk=model1.pk)
model2count = (
Model1.objects.annotate(count=Count('model2'))
.filter(pk=model1.pk)
.values_list('title', 'count', named=True)
)
template.html
{% for object in model2count %}
{{ object.title }}
{{ object.count }}
{% endfor %}

django formset for 3 foreign keys giving me error of [u'ManagementForm data is missing or has been tampered with']

I am trying to make a formset of 3 foreign keys. Servicer, Sdescrition, and Stag. For some reason it is give me this error
ManagementForm data is missing or has been tampered with
I am pretty new at coding, if there is a better way than inlineform_factory please let me know and suggest. Thank you very much. I am pretty much trying to connect three foreign keys together put them into a single form page on the webpage. Once again, Thank you
Views.py
def home(request):
title = 'Please Enter Company Information'
SdescriptionFormSet = inlineformset_factory( Servicer, Sdescription, form = InputFormSdescription, extra=1)
StagFormSet = inlineformset_factory( Sdescription, Stag, form = InputFormStag, extra=1) #new
Sdescription_formset = InputFormSdescription()
Stag_formset = StagFormSet()
if request.POST:
FormServicer = InputFormServicer(request.POST or None)
FormSdescription = InputFormSdescription(request.POST or None) #new
if FormServicer.is_valid() and FormSdescription.is_valid():
servicer = FormServicer.save(commit=False)
Sdescription_formset=SdescriptionFormSet(request.POST, instance =servicer)
sdescription = FormSdescription.save(commit=False) #new
Stag_formset=StagFormSet(request.POST, instance =sdescription) #new
if Sdescription_formset.is_valid():
servicer.save()
Sdescription_formset.save()
sdescription.save() #n
Stag_formset.save() #new
return HttpResponseRedirect(reverse('serv.views.home'))
else:
FormServicer = InputFormServicer()
Sdescription_formset = SdescriptionFormSet(instance=Servicer())
Stag_formset=StagFormSet(instance=Sdescription())
context = {
"title": title,
"FormServicer": FormServicer,
"Sdescription_formset": Sdescription_formset,
"Stag_formset": Stag_formset, #new
}
return render(request, 'serv.html', context)
Models.py:
class Servicer(models.Model):
servicer_name = models.CharField(max_length=45,unique=True)
pub_date = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.servicer_name
def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
def was_published_recently(self):
now = timezone.now()
return now - datetime.timedelta(days=1) <= self.pub_date <= now
was_published_recently.admin_order_field = 'pub_date'
was_published_recently.boolean = True
was_published_recently.short_description = 'Published recently?'
# adds view site button on admin.
def get_absolute_url(self):
return '/video/%s/' % self.id
class Sdescription(models.Model):
servicer = models.ForeignKey(Servicer, on_delete=models.CASCADE, blank=True, null=True)
address = models.CharField(max_length=100,blank=True, null=True, unique=True)
address_line_2= models.CharField(max_length=100, blank=True, null=True)
city= models.CharField(max_length=50, blank=True, null=True)
state= models.CharField(max_length=25, blank=True, null=True)
zip = models.CharField(max_length=15, blank=True, null=True)
near_landmark = models.CharField(max_length=200, blank=True, null=True)
main_contact_name = models.CharField(max_length=20, blank=True, null=True)
phone_number1 = models.CharField(max_length=20, blank=True, null=True)
phone_number2 = models.CharField(max_length=20, blank=True, null=True)
comments = models.TextField(max_length=200, blank=True, null=True)
website = models.CharField(max_length=40, blank=True, null=True)
email = models.EmailField(max_length = 20,blank=True, null=True)
image = models.ImageField(upload_to='uploads/',blank=True, null=True)
def __str__(self):
return '%s %s %s %s %s %s %s %s %s %s %s' % (self.address,
self.address_line_2, self.city, self.state, self.zip,
self.near_landmark, self.phone_number1, self.phone_number2, self.comments,
self.website, self.email)
class Stag(models.Model):
tag_name = models.CharField(max_length=30,blank=True, null=True)
price = models.DecimalField(max_digits=9, decimal_places=2,blank=True,
null=True)
comments = models.CharField(max_length=100,blank=True, null=True)
sdescription = models.ForeignKey(Sdescription,
on_delete=models.CASCADE,blank=True, null=True)
def __str__(self):
return '%s $%s %s' % (self.tag_name, self.price, self.comments)
Forms.py:
class InputFormServicer(forms.ModelForm):
class Meta:
model = Servicer
fields=['servicer_name']
class InputFormSdescription(forms.ModelForm):
class Meta:
model = Sdescription
fields=['address','address_line_2','city','state','zip','near_landmark',
'main_contact_name', 'phone_number1', 'phone_number2', 'comments',
'website', 'email','image'
]
class InputFormStag(forms.ModelForm):
class Meta:
model = Stag
fields=['tag_name','price','comments']
Serv.html:
<h1>{{title}}</h1>
<form method='POST' action=''> {% csrf_token %}
{{FormServicer}}
{{Sdescription_formset.as_p}}
{{Sdescription_formset.management_form}}
{{Stag_formset.as_p}}
{{Stag_formset.management_form}}
<input type='submit' value='save'/>
</form>

Display ForeignKey attributes in template

I have the following models:
# models.py
class Site(models.Model):
name = models.CharField(max_length=75)
slug = models.SlugField(_('slug'), max_length=75, blank=True, null=True)
link = models.CharField(max_length=150)
created_on = models.DateTimeField(auto_now_add=True, editable=False)
modified_on = models.DateTimeField(auto_now=True)
class SiteRatings(models.Model):
site = models.ForeignKey('Site', related_name='siterating', blank=True, null=True)
overall_rating = models.FloatField(blank=True, null=True)
plans_rating = models.FloatField(blank=True, null=True)
prices_rating = models.FloatField(blank=True, null=True)
design_rating = models.FloatField(blank=True, null=True)
support_rating = models.FloatField(blank=True, null=True)
def save(self, *args, **kwargs):
self.overall_rating = (self.plans_rating + self.prices_rating + self.design_rating + self.support_rating)/4
super(SiteRatings, self).save(*args, **kwargs)
def __str__(self):
return self.site.name
My views
# views.py
def home(request):
print(user_language)
site_list = Site.objects.order_by('-date_launched')
return render_to_response('index.html', {'site_list': site_list}, RequestContext(request))
In template
# template
{% for site in site_list %}
<h4><span class="label label-info">{{ site.name }}</span></h4>
<h4><span class="label label-info">{{ site.siterating.overall_rating }}</span></h4>
{% endfor % }
The problem is that when I try to access {{ site.siterating.overall_rating }} nothing is being returned, I tried to change for {{ site.siterating.0.overall_rating }} and it is not displaying yet.
I solved my problem. First in models I created a method to get the site rating (thank you karthikr for your suggestion):
#models
class Site(models.Model):
name = models.CharField(max_length=75)
slug = models.SlugField(_('slug'), max_length=75, blank=True, null=True)
link = models.CharField(max_length=150)
created_on = models.DateTimeField(auto_now_add=True, editable=False)
modified_on = models.DateTimeField(auto_now=True)
def get_site_rating(self):
return self.siterating.filter(site = self)
After, I went to my template and used the class method that I had created:
{% for site in site_list %}
<h4><span class="label label-info">{{ site.name }}</span></h4>
<h4><span class="label label-info">{{ site.get_site_rating.0.overall_rating }}</span></h4>
{% endfor % }
Please, note that I used ...get_site_rating.0... just because in my case I have just 1 rating/site.