I am trying the tutorial for Django on making a local library website with books.
One of the challenges is to make an author detail view which lists all the books that author has written. I am having trouble displaying any book information for the author.
Model.py
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
from django.urls import reverse
import uuid
# Create your models here.
class Genre(models.Model):
"""
Model resprenting the book genre
"""
name=models.CharField(max_length=200, help_text="Enter a book genre")
def __str__(self):
return self.name
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey('Author', on_delete=models.SET_NULL, null=True)
summary = models.TextField(max_length=1000, help_text="Enter a brief description")
isbn = models.CharField('ISBN', max_length=13, help_text="13 character ISBN field")
genre = models.ManyToManyField(Genre, help_text="Select a genre for this book")
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('book-detail', args=[str(self.id)])
def display_genre(self):
return ', '.join([genre.name for genre in self.genre.all()[:3] ])
display_genre.short_description = 'Genre'
class BookInstance(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, help_text="Unique book number")
book = models.ForeignKey('Book', on_delete=models.SET_NULL, null=True)
imprint = models.CharField(max_length=200)
due_back = models.DateField(null=True, blank=True)
LOAN_STATUS = (
('d', 'Maintenance'),
('o', 'On loan'),
('a', 'Available'),
('r', 'Reserved'),
)
status = models.CharField(max_length=1, choices=LOAN_STATUS, blank=True, default='d', help_text="Book Availability")
class Meta:
ordering = ['due_back']
def __str__(self):
return ('%s (%s)' %(self.id, self.book.title))
class Author(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
date_of_birth = models.CharField(max_length=10, null=True, blank=True)
date_of_death = models.CharField(max_length=10, null=True, blank=True)
def get_absolute_url(self):
return reverse('author-detail', args=[str(self.id)])
def __str__(self):
return ('%s, %s' % (self.last_name, self.first_name))
def display_books(self):
books = Book.objects.get(pk=self.id)
return books.book_set.all()
display_books.short_description = 'Books'
author_detail.html
Really lost here!
{% extends "base.html" %}
{% block content %}
<h1>Author: {{ author }}</h1>
<p><strong>Date of Birth:</strong> {{ author.date_of_birth }}</p>
<p><strong>Date of Death:</strong> {{ author.date_of_death }}</p>
<!--<p><strong>Books:</strong> {% for books in book.author.pk.all %} {{ book.author }}{% if not forloop.last %}, {% endif %}{% endfor %}</p> -->
<div style="margin-left:20px;margin-top:20px">
<h4>Books</h4>
{% for books in book.all %}
<p>{{ books }} Testing Vars {{ book.author_set }} Get copies from key {{ book.author.pk }} </p>
{% endfor %}
</div>
{% endblock %}
Views.py
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.shortcuts import render
from django.views import generic
# Create your views here.
from .models import Book, Author, BookInstance, Genre
def index(request):
num_books=Book.objects.all().count()
num_instances=BookInstance.objects.all().count()
#available books
num_instances_available=BookInstance.objects.filter(status__exact='a').count()
num_authors=Author.objects.count()
return render(request,
'index.html',
context={'num_books': num_books, 'num_instances': num_instances,
'num_instances_available' : num_instances_available, 'num_authors': num_authors},
)
class BookListView(generic.ListView):
model = Book
class BookDetailView(generic.DetailView):
model = Book
class AuthorListView(generic.ListView):
model = Author
class AuthorDetailView(generic.DetailView):
model = Author
Assuming author_detail.html is used in AuthorDetailView ListView, I'd suggest some changes in your template, maybe like this,
{% block content %}
<h1>Author: {{ object.first_name }} {{ object.last_name }}</h1>
<p><strong>Date of Birth:</strong> {{ object.date_of_birth }}</p>
<p><strong>Date of Death:</strong> {{ object.date_of_death }}</p>
<div style="margin-left:20px;margin-top:20px">
<h4>Books</h4>
{% for book in object.book_set.all %}
<p>{{ book.title }}</p>
{% endfor %}
</div>
{% endblock %}
Since, you haven't overridden the get_context_data() method of the ListView, the default context_variable_name would be object. You may need to refer the Author instance by object.
Related
I'm a newbie to Django, trying to build site to allow people to register for football matches.
At the moment, a user can register multiple times for the same match, which is obviously not ideal! Is there a way that I can identify if the currently logged in user has already registered, and then replace the register button with a message telling them that they have already registered? I guess I need some kind of Boolean value in my EventDetail view, and then I can make a conditional statement in the template, but I'm unsure as to how to implement this. I hope this question is clear, it's my very first post!
views.py:
class EventDetail(View):
def get(self, request, id, *args, **kwargs):
event = get_object_or_404(Event, pk=id)
registrations = Registration.objects.filter(event=event)
total_participants = 0
for person in registrations:
total_participants += 1
if person.guest:
total_participants += 1
remaining_spaces = event.max_participants - total_participants
template = "event_detail.html"
context = {
"event": event,
"total_participants": total_participants,
"registrations": registrations,
"remaining_spaces": remaining_spaces,
}
return render(request, template, context)
template
{% extends "base.html" %}
{% block content %}
<p>{{ event.title }}</p>
<p>{{ total_participants }} / {{ event.max_participants }} ({{ remaining_spaces }} spot{{ remaining_spaces|pluralize }}
remaining!)</p>
{% if total_participants < event.max_participants %}
Register
{% else %}
<p>This event has filled up.</p>
{% endif %}
<h2>Current Participants</h2>
<ul>
{% for person in registrations %}
<li>
{{ person.name }}
{% if person.guest %}
+1
{% endif %}
</li>
{% endfor %}
</ul>
{% endblock %}
models.py
from django.db import models
from django.contrib.auth.models import User
class Event(models.Model):
title = models.CharField(max_length=100)
created_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name="event_posts")
event_date_and_time = models.DateTimeField()
venue = models.CharField(max_length=100)
max_participants = models.IntegerField()
extra_info = models.TextField(blank=True)
updated_on = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['event_date_and_time']
def __str__(self):
return self.title
class Registration(models.Model):
event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="event_registration")
name = models.ForeignKey(User, on_delete=models.CASCADE)
ball = models.BooleanField(default=False)
bibs = models.BooleanField(default=False)
guest = models.BooleanField(default=False)
def __str__(self):
return str(self.name)
I'm not sure how to ask this question but I'll give it a shot.
I am writing a Todo application and want to display each todo under its respective category in the template. For example
Display each category
{% for category in categories %}
<h2>{{ category.name }}</h2>
Now show each todo that falls under the above category
{% for todo in todos %}
<p>{{ todo.description }}</p>
{% endfor %}
{% endfor %}
How do I create a queryset that will give my this type of structure? Or is there are different way to achieve this?
If something is unclear or require more info let me know and I'll add it to the post
Any help is appreciated, thank you.
Models
class Category(models.Model):
name = models.CharField(max_length=20)
class Meta:
verbose_name_plural = "Categories"
def __str__(self):
return self.name
class Todo(models.Model):
# Priority choices
PRIORITY_CHOICES = (
("bg-danger", "High"),
("bg-info", "Normal"),
("bg-warning", "Low"),
)
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
description = models.CharField(max_length=255)
priority = models.CharField(max_length=200, choices=PRIORITY_CHOICES, null=True)
completed = models.BooleanField(default=False)
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
category = models.ManyToManyField(Category)
def __str__(self):
return self.description
View
def todo_listview(request):
template_name = "todos/listview.html"
context = {
"todos": get_users_todos(user=request.user),
"categories": Category.objects.all(),
}
return render(request, template_name, context)
You can prefetch the user's Todos, with:
from django.db.models import Prefetch
def todo_listview(request):
template_name = 'todos/listview.html'
context = {
'categories': Category.objects.prefetch_related(
Prefetch('todo_set', queryset=get_users_todos(user=request.user), to_attr='user_todos')
)
}
return render(request, template_name, context)
and then render this with:
Display each category
{% for category in categories %}
<h2>{{ category.name }}</h2>
{% for todo in category.user_todos %}
<p>{{ todo.description }}</p>
{% endfor %}
{% endfor %}
Since there is a many-to-many field between Category and Todo, it is possible that the same Todo will be printed multiple times: once per category.
Note: The documentation advises to
use the AUTH_USER_MODEL setting [Django-doc] over
get_user_model() [Django-doc].
This is safer, since if the authentication app is not yet loaded, the settings
can still specify the name of the model. Therefore it is better to write:
from django.conf import settings
class Todo(models.Model):
# …
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE
)
I have created a project and added models in the models and also created a form in forms.py. I am trying to modify some values using the commit=False. On submitting the form, it's not saving the data to the database and there is no error thrown from the code. I have no idea on how I can solve the issue.
models.py
from django.db import models
from django.contrib.auth.models import User
class Agent(models.Model):
name = models.CharField(max_length=255)
id_no = models.CharField(max_length=20)
address = models.CharField(max_length=255)
gender = models.CharField(max_length=8)
age = models.IntegerField()
agent_no = models.CharField(unique=True, max_length=12, null=True)
email = models.EmailField(max_length=50)
date_created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"{self.name}"
class Company(models.Model):
user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)
name = models.CharField(max_length=255, null=True, blank=True)
address = models.CharField(max_length=255, null=True, blank=True)
telephone = models.IntegerField( null=True, blank=True)
email = models.EmailField(max_length=50, null=True, blank=True)
date_created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"{self.name}"
class Task(models.Model):
company = models.ForeignKey(Company, null=True, on_delete=models.SET_NULL)
agent = models.ForeignKey(Agent, null=True, on_delete=models.SET_NULL)
cargo = models.CharField(max_length=200)
document = models.FileField(upload_to='documents/')
quantity = models.FloatField()
amount = models.FloatField()
duty = models.FloatField(null=True, blank=True)
status = models.CharField(max_length=50, default='Pending')
date_created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"{self.cargo}"
forms.py
from django.contrib.auth.models import User
from django import forms
import re
from django.core.exceptions import ValidationError
from .models import *
class TaskForm(forms.ModelForm):
cargo = forms.CharField( required=False)
document = forms.FileField()
quantity = forms.FloatField()
amount = forms.FloatField()
class Meta:
model = Task
fields = ['cargo', 'document', 'quantity', 'amount']
views.py
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
from .forms import *
from django.contrib.auth.models import Group
from django.contrib.auth import login, authenticate
#login_required(login_url='/login/')
def task(request):
if request.method == "POST":
form=TaskForm(request.POST)
if form.is_valid():
m = form.save(commit=False)
m.status ='Pending'
m.company = request.user.username
m.save()
else:
form= TaskForm(None)
context = {'form':form}
return render(request, 'registration/templates/task.html', context)
template.html
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<form action="" method="post">
{% csrf_token %}
<div class="container my-2 d-flex" style="align-items:center; justify-content:center;">
<div class="card d-flex" style="width:70%; align-items:center; justify-content:center;">
<div class="form-control pt-3 " style="text-transform:uppercase;color:green"><h5>Upload trip information
</h5>
</div>
<div class="card-body" style="width:98%">
{{ form|crispy}}
</div>
<div class="card-footer" style="width:98%">
<input type="submit" value="Upload Information" class="btn btn-outline-secondary btn-sm">
</div>
</div>
</div>
</form>
{% endblock %}
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
from django.urls import reverse
from django.http import HttpResponseRedirect
from .forms import *
from django.contrib.auth.models import Group
from django.contrib.auth import login, authenticate
#login_required(login_url='/login/')
def task(request):
if request.method == "POST":
form=TaskForm(request.POST,request.FILES) # this is important if you are dealing with files
if form.is_valid():
m = form.save(commit=False)
m.status ='Pending'
m.company = request.user.username
m.save()
return HttpResponseRedirect(reverse(' the url of where you want to redirect the user '))
else:
form= TaskForm()
context = {'form':form}
return render(request, 'registration/templates/task.html', context)
and in your templates try to add enctype in your form.
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="container my-2 d-flex" style="align-items:center; justify-content:center;">
<div class="card d-flex" style="width:70%; align-items:center; justify-content:center;">
<div class="form-control pt-3 " style="text-transform:uppercase;color:green"><h5>Upload trip information
</h5>
</div>
<div class="card-body" style="width:98%">
{{ form|crispy }}
</div>
<div class="card-footer" style="width:98%">
<input type="submit" value="Upload Information" class="btn btn-outline-secondary btn-sm">
</div>
</div>
</div>
</form>
{% endblock %}
Sorry for this title, I wasn't sure how to name it properly. I'm having problem with getting queryset of ManyToManyField that is in relation with other ManyToManyField. So it's look like this, there is model Company that has ManyToManyField with Person and Person model got ManyToManyField with Position, because logic behind it is that 1 company can have many persons and 1 person can have few positions and can be employed by few companies (which is clear I think). I'm getting the queryset for Person in Company by this way
{% for team in brand.team.all %}
<p>{{ team.first_name }} {{ team.last_name }}</p>
<img class="img-thumbnail" src="/media/{{ team.photo }}">
<p>{{ team.position }} </p>
<p>{{ team.about }} </p>
{% endfor %}
And I'm getting what I want, comparing this to template looks like this
but I'm not getting positions of person, only company.Position.None and I've no idea how to get this. From documentation so far I know that it works for single ManyToManyField but I couldn't find example similar to mine case and I'm not sure how I should get (person's position)
Below are my files
models.py
from django.db import models
...
TYPES = (
('Startup', 'Startup'),
... )
CITIES = (
('Warszawa', 'Warszawa'),
... )
STACK = (
('PHP', 'PHP'),
... )
STUDENTS = (
('No', 'No'),
('Yes', 'Yes')
)
STACK_ICONS = (
('/static/icons/stack/php.png', 'PHP'),
('/static/icons/stack/javascript.png', 'JavaScript'),
...
)
POSITIONS = (
('CEO', 'Chief Executive Officer'),
('CTO', 'Chief Technology Officer'),
...
)
# object position with relationship many to many to person
class Position(models.Model):
position = models.CharField(max_length=50, choices=POSITIONS)
def __str__(self):
return self.position
# object person relation many to one (many persons to one company)
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
about = models.TextField(max_length=500, default=None)
position = models.ManyToManyField('Position')
photo = models.ImageField(blank=True, null=True, default=None)
def __str__(self):
return "%s %s" % (self.first_name, self.last_name)
# object company
class Company(models.Model):
# field person with relation many to one (many persons to 1 company)
team = models.ManyToManyField('Person')
name = models.CharField(max_length=100, blank=False)
technologies = models.ManyToManyField('Stack')
website = models.TextField(max_length=150, blank=True, null=True, validators=[URLValidator()])
...
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(Company, self).save(*args, **kwargs)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.name
# object stack relation manytomany with Company
class Stack(models.Model):
name = models.CharField(max_length=30, choices=STACK)
icon = models.CharField(max_length=80, choices=STACK_ICONS)
def __str__(self):
return self.name
views.py
from django.shortcuts import render, get_object_or_404, redirect
...
def brands(request, slug):
brand = get_object_or_404(Company, slug=slug)
return render(request, 'company/comp_view.html', {'brand': brand})
def stacks(request):
stack = get_object_or_404(Stack)
return render(request, 'company/comp_view.html', {'stack': stack})
def positions(request):
position = get_object_or_404(Position)
return render(request, 'company/comp_view.html', {'position': position})
...
comp_view.html
{% extends 'company/base.html' %}
<div class="col col-md-1"></div>
<div class="col col-md-5 card-section">
<div class="team ">
<div class="card-title">
<span>Team</span>
</div>
<div class="row text-center">
<div class="col col-md-4">
{% for team in brand.team.all %}
<p>{{ team.first_name }} {{ team.last_name }}</p>
<img class="img-thumbnail" src="/media/{{ team.photo }}">
<p>{{ team.position }}</p>
<p>{{ team.about }} </p>
</div>
{% endfor %}
</div>
</div>
{% endblock %}
There's no such thing as a "single ManyToManyField". You have an m2m relationship, you need to iterate through it just like you do for the team members.
{% for position in team.position.all %}
{{ position.name }}
{% endfor %}
every one I am trying the solr and django-haystack to make my search,,I can see there are 9 Docs in my solr,,,and in my db there are 9 data actually,,however,,when it comes to search form and search ,,I got no result ,,
I have to search designer
solr
models.py
# -*- coding: utf-8 -*-
from django.db import models
from django.contrib.auth.models import User
from PIL import Image
from django.utils import timezone
.....
class ProductsTbl(models.Model):
model_number = models.CharField(max_length=255, blank=True, unique=True,error_messages={'unique':"這 model number 已經被註冊了 ."})
name = models.CharField(max_length=255, blank=True, null=True)
material = models.CharField(max_length=255,blank=True, null=True)
color = models.CharField(max_length=255, blank=True, null=True)
feature = models.TextField(blank=True, null=True)
created = models.DateTimeField(editable=False)
modified = models.DateTimeField(auto_now=True)
release = models.DateTimeField(blank=True, null=True)
twtime = models.DateTimeField(blank=True, null=True)
hktime = models.DateTimeField(blank=True, null=True)
shtime = models.DateTimeField(blank=True, null=True)
jptime = models.DateTimeField(blank=True, null=True)
suggest = models.TextField(blank=True, null=True)
description = models.TextField(blank=True, null=True)
cataloggroup = models.ManyToManyField(CatalogGroup)
place = models.ManyToManyField(Place)
scale = models.ManyToManyField(Scale)
slug = models.SlugField(unique=True)
user = models.ForeignKey(User, blank=True, null=True)
useredit = models.CharField(max_length=32, blank=True, null=True)
image = models.ImageField(upload_to=get_imagep_Product, blank=True)
def __unicode__(self):
return self.name
def save(self, *args, **kwargs):
''' On save, update timestamps '''
if not self.id:
self.created = timezone.now()
return super(ProductsTbl, self).save(*args, **kwargs)
views.py
.....
from haystack.query import SearchQuerySet
.....
def post_search(request):
form = SearchForm()
if 'query' in request.GET:
form = SearchForm(request.GET)
if form.is_valid():
cd = form.cleaned_data
results = SearchQuerySet().models(ProductsTbl).filter(content=cd['query']).load_all()
# count total results
total_results = results.count()
return render(request, 'search.html', {'form': form,
'cd': cd,
'results': results,
'total_results': total_results})
return render(request, 'search.html', {'form': form,})
settings.py
.......
INSTALLED_APPS = (
.....
'haystack',
.....
)
....
HAYSTACK_CONNECTIONS = {
'default': {
'ENGINE': 'haystack.backends.solr_backend.SolrEngine',
'URL': 'http://127.0.0.1:8983/solr/designer'
},
}
I guess probably search_indexes.py not correct
search_indexes.py
import datetime
from haystack import indexes
from .models import ProductsTbl
class ProductsTblIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
author = indexes.CharField(model_attr='user')
# created = indexes.DateTimeField(model_attr='created')
def get_model(self):
return ProductsTbl
# def index_queryset(self, using=None):
# """Used when the entire index for model is updated."""
# return self.get_model().objects.filter(created__lte=datetime.datetime.now())
search/indexes/designer/productstbl_text.txt
{{ object.title }}
{{ object.user.get_full_name }}
{{ object.body }}
search.html
{% block title %}Search{% endblock %}
{% block content %}
{% if "query" in request.GET %}
<h1>Posts containing "{{ cd.query }}"</h1>
<h3>Found {{ total_results }} result{{ total_results|pluralize }}</h3>
{% for result in results %}
{% with post=result.object %}
<h4>{{ post.title }}</h4>
{{ post.body|truncatewords:5 }}
{% endwith %}
{% empty %}
<p>There are no results for your query.</p>
{% endfor %}
<p>Search again</p>
{% else %}
<h1>Search for posts</h1>
<form action="." method="get">
{{ form.as_p }}
<input type="submit" value="Search">
</form>
{% endif %}
{% endblock %}
after I run python manage.py rebuild_index
here is my data in mysql
there are 9 data ,,just like the solr shows 9 docs,,,
however, after I run the search form ,,,it shows no result
the solr console just shows