Django create parent model and child model in one form - python

I am learning Python and Django, and I am trying to create a recipe with a form. The problem is I have two models, Recipe and RecipeIngredient and I don't know how to add recipe ingredients to the form since it requires the primary key of the recipe and from what I understand, the key is not created until after the form is saved? So how can I create a recipe with both the Recipe and RecipeIngredient when Recipe is not yet initialized?
Models.py:
class Recipe(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
image = models.ImageField(upload_to='image/', blank=True, null=True)
name = models.CharField(max_length=220) # Lasanga
description = models.TextField(blank=True, null=True)
notes = models.TextField(blank=True, null=True)
cookTime = models.CharField(max_length=50, blank=True, null=True)
timeStamp = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
active = models.BooleanField(default=True)
#property
def title(self):
return self.name
def get_absolute_url(self):
return reverse("recipes:detail", kwargs={"id": self.id}) # recipes is from url.py app_name
def get_hx_url(self):
return reverse("recipes:hx-detail", kwargs={"id": self.id}) # recipes is from url.py app_name
def get_edit_url(self):
return reverse("recipes:update", kwargs={"id": self.id})
def get_image_upload_url(self):
return reverse("recipes:recipe-ingredient-image-upload", kwargs={"parent_id": self.id})
def get_delete_url(self):
return reverse("recipes:delete", kwargs={"id": self.id})
def get_ingredients_children(self):
return self.recipeingredient_set.all()
def get_instruction_children(self):
return self.recipeinstruction_set.all()
class RecipeIngredient(models.Model):
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE)
name = models.CharField(max_length=220) # grilled chicken pasta
description = models.TextField(blank=True, null=True)
quantity = models.CharField(max_length=50, blank=True, null=True)
unit = models.CharField(max_length=50, validators=[validate_unit_of_measure], blank=True, null=True)
instructions = models.TextField(blank=True, null=True)
timeStamp = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
active = models.BooleanField(default=True)
def get_absolute_url(self):
return self.recipe.get_absolute_url() # recipe cannot be none
def get_hx_edit_url(self):
kwargs = {
"parent_id": self.recipe.id,
"id": self.id
}
return reverse("recipes:hx-ingredient-detail", kwargs=kwargs)
def get_delete_url(self):
kwargs = {
"parent_id": self.recipe.id,
"id": self.id
}
return reverse("recipes:ingredient-delete", kwargs=kwargs)
Views.py
#login_required
def recipe_create_view(request):
form = RecipeForm(request.POST or None)
context = {
"form": form,
}
if form.is_valid():
obj = form.save(commit=False)
obj.user = request.user
obj.save()
if request.htmx: # necessary to pass headers from htmx response if we want the django to recognise the htmx change
headers = {
"HX-Redirect": obj.get_absolute_url()
}
return HttpResponse("Created", headers=headers)
# if request.htmx: # could use this but the url doesn't update, stays as create, would need to use HX-Push header & HttpResponse + context somehow
# context = {
# "object": obj
# }
# return render(request, "recipes/partials/detail.html", context)
return redirect(obj.get_absolute_url())
return render(request, "recipes/create.html", context)
#login_required
def recipe_ingredient_update_hx_view(request, parent_id=None, id=None): # this is both create & edit, can create
if not request.htmx:
raise Http404
try:
parent_obj = Recipe.objects.get(id=parent_id, user=request.user)
except:
parent_obj = None
if parent_obj is None:
return HttpResponse("Not Found.")
instance = None
if id is not None:
try:
instance = RecipeIngredient.objects.get(recipe=parent_obj, id=id) # think of this as an object if that helps
except:
instance = None
form = RecipeIngredientForm(request.POST or None, instance=instance)
url = reverse("recipes:hx-ingredient-create", kwargs={"parent_id": parent_obj.id})
if instance:
url = instance.get_hx_edit_url()
context = {
"url": url,
"form": form,
"object": instance
}
if form.is_valid():
new_obj = form.save(commit=False)
if instance is None:
new_obj.recipe = parent_obj
new_obj.save()
context['object'] = new_obj # because it's possible the object/instance in None
return render(request, "recipes/partials/ingredient-inline.html", context)
return render(request, "recipes/partials/ingredient-form.html", context)
forms.py
from django import forms
from .models import Recipe, RecipeIngredient
class RecipeForm(forms.ModelForm):
class Meta:
model = Recipe
fields = ['name', 'image', 'description', 'notes', 'cookTime']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in self.fields:
new_data = {
"placeholder": f"Recipe {str(field)}",
"class": "form-control",
}
self.fields[str(field)].widget.attrs.update(new_data)
class RecipeIngredientForm(forms.ModelForm):
class Meta:
model = RecipeIngredient
fields = ['name', 'quantity', 'unit']
urls.py
from django.urls import path
from .views import (
recipe_list_view,
recipe_delete_view,
recipe_create_view,
recipe_update_view,
recipe_detail_hx_view,
recipe_ingredient_update_hx_view,
recipe_ingredient_delete_view,
recipe_ingredient_image_upload_view,
recipe_ingredient_url_scrape_view
)
app_name='recipes' # allows use of recipes:list as a reverse url call
urlpatterns = [
path('', recipe_list_view, name='list'), # index / home / root
path('create/>', recipe_create_view, name='create'),
path('hx/<int:parent_id>/ingredient/<int:id>', recipe_ingredient_update_hx_view, name='hx-ingredient-detail'), #or detail
path('hx/<int:parent_id>/ingredient/', recipe_ingredient_update_hx_view, name='hx-ingredient-create'),
]
create.html
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% load static %}
{% block content %}
<div class="container-fluid px-5">
<h1 class="pb-5">Create Recipe</h1>
<div id="recipe-container">
<form action='.' method="POST" hx-post='.'>
{% csrf_token %}
<div class='row'>
<div class="row d-flex pb-5">
<div class="col-12 col-lg-6 justify-content-center d-flex order-first order-lg-last pictureBox"
style="height: 400px; width:450; border: solid tomato 1px;">
<div class="align-self-center">
{{ form.image|as_crispy_field }}
</div>
</div>
<div class="col-12 col-lg-6 order-lg-first">
<div class="pb-3">{{ form.name|as_crispy_field }}</div>
<div class="pb-3">{{ form.description|as_crispy_field }}</div>
</div>
</div>
<div class="col-12 col-md-6">
{{ form.notes|as_crispy_field }}
</div>
<div class="col-12 col-md-6">
{{ form.cookTime |as_crispy_field }}
</div>
</div>
</div>
<div class='col-12 col-md-4'>
<!-- ADD INGREDIENTS ? -->
</div>
<div class="htmx-indicator">Loading...</div>
<div class="d-flex">
<button class="btn btn-success htmx-inverted-indicator" style='margin-top:10px;' type='submit'>Save</button>
<a class="btn btn-danger" href='{% url "recipes:list" %}'>Delete</a>
</div>
{% if message %}
<p>{{ message }}</p>
{% endif %}
</form>
</div>
</div>
{% endblock content %}
ingredient-form.html
<form action='.' method="POST" hx-post='{% if url %} {{ url }} {% else %} . {{% endif %}' hx-swap='outerHTML'>
{% csrf_token %}
{{ form.as_p}}
<div class="htmx-indicator">Loading...</div>
<button class="htmx-inverted-indicator" style='margin-top:10px;' type='submit' >Save</button>
</form>
ingredient-inline.html
<div class="py-1" id="ingredient-{{object.id}}">
<p>{% if not edit %} <b>{{ object.quantity }} {% if object.unit %} {{ object.unit }} {% endif %}</b> {% else %} {{ object.quantity }} {{ object.unit }} {% endif %} - {{ object.name }}</p>
{% if edit %}
<button class="btn btn-primary" hx-trigger='click' hx-get='{{ object.get_hx_edit_url }}' hx-target="#ingredient-{{object.id}}">Edit</button> <!-- target will replace whole div-->
<button class="btn btn-danger" href='{{ object.get_delete_url }}' hx-post='{{ object.get_delete_url }}' hx-confirm="Are you sure you want to delete {{ object.name }}?" hx-trigger='click' hx-target="#ingredient-{{object.id}}" hx-swap="outerHTML">Delete</button>
{% endif %}
</div>

The key to this problem is using a formset, as you will likely want to save multiple ingredients to the recipe. Django documentation outlines how to use them. Your view would end up looking something like below, allowing you to save the parent model, which will give you the parent instance/primary key to then save the ingredients.
def recipe_create_view(request):
form = RecipeForm(request.POST or None)
RecipeIngredientFormset = formset_factory(RecipeIngredientForm)
formset = RecipeIngredientFormset(request.POST or None)
context = {
"form": form,
"formset": formset,
}
if request.method == "POST":
if form.is_valid() and formset.is_valid():
parent = form.save(commit=False)
parent.user = request.user
parent.save()
#recipe ingredients
for form in formset:
child = form.save(commit=False)
child.recipe = parent
child.save()

Related

How to add comments on django

I am trying to add a comment system to my project, all the code looks fine but I am getting this error "ValueError at /
The QuerySet value for an exact lookup must be limited to one result using slicing". I dont know what is wrong but the error might be on the views.py file.
views.py
def imagelist(request):
images = Post.objects.all()
post = get_object_or_404(Post)
comments = Comment.objects.filter(post=images)
if request.method == 'POST':
comment_form = CommentForm(request.POST or None)
if comment_form.is_valid():
contentt = request.POST.get('content')
comment = Comment.objects.create(post=images, user=request.user, content=content)
comment.save()
return HttpResponseRedirect(post.get_absolute_url())
else:
comment_form = CommentForm()
context2 = {
"images": images,
"comments": comments,
"comment_form": comment_form,
}
return render(request, 'imagelist.html', context2)
models.py
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
content = models.TextField(max_length=160)
timestamp = models.DateTimeField(auto_now_add=True)
def __str__(self):
return '{}-{}'.format(self.post.title.str(self.user.username))
class Post(models.Model):
text = models.CharField(max_length=200)
posti = models.ImageField(upload_to='media/images', null=True, blank="True")
video = models.FileField(upload_to='media/images', null=True, blank="True")
user = models.ForeignKey(User, related_name='imageuser', on_delete=models.CASCADE, default='username')
liked = models.ManyToManyField(User, default=None, blank=True, related_name='liked')
updated = models.DateTimeField(auto_now=True)
created =models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.tittle)
forms.py
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('content',)
You need to pass to the create mathod of the comments a single Post since the corresponding fields is a ForeignKey and you're passing an entire queryset (Post.objects.all())
You need get just the post where the comment should live.
single_post = Post.objects.get(pk=the_post_pk)
comment = Comment.objects.create(post=single_post, user=request.user, content=content)
The problem is that you write:
images = Post.objects.all()
comments = Comment.objects.filter(post=images)
Here images is a set of Post objects, not a single Post object, hence you can not filter on that. But you actually do not need to do this anyway.
Furthermore there is also a small mistake in the __str__ of your Comment model:
class Comment(models.Model):
# …
def __str__(self):
return '{}-{}'.format(self.post.text, self.user.username)
But the view itself looks "odd", since you here write a list of Posts, but if you make a POST request, you will somehow need to know to what post you want to submit the Comment, therefore at least the view that accepts the POST request, will need to know the primary key of the Post to comment on. You can for example encode that in the urls.py:
# appname/urls.py
from django.urls import path
from app import views
urlpatterns = [
path('post/<int:pk>/', views.post_detail, name='post_detail'),
path('post/', views.post_list, name='post_detail')
]
In that view, we can then fetch the item, for example with get_object_or_404, and in case of a POST set the post and user objects in the view:
from django.shortcuts import redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
#login_required
def post_detail(request, pk):
image = get_object_or_404(Post, pk=pk)
if request.method == 'POST':
comment_form = CommentForm(request.POST, request.FILES)
if comment_form.is_valid():
comment_form.instance.user = request.user
comment_form.instance.post = image
comment_form.save()
return redirect(post)
else:
comment_form = CommentForm()
context = {
'post': image,
'comment_form': comment_form,
}
return render(request, 'imagedetail.html', context)
In your template, you can render the comments of the post with:
{{ post }}
{% for comment in post.comment_set.all %}
{{ comment }}
{% endfor %}
<form method="post" action="{% url 'post_detail' pk=post.pk %}">
{% csrf_token %}
{{ comment_form }}
</form>
you can also make a view that renders a list:
#login_required
def post_list(request, pk):
images = Post.objects.prefetch_related('comment_set')
comment_form = CommentForm()
context = {
'image': images,
'comment_form': comment_form,
}
return render(request, 'imagelist.html', context)
in the template for the list, you can render it with:
{% for post in images %}
{{ post }}
{% for comment in post.comment_set.all %}
{{ comment }}
{% endfor %}
<form method="post" action="{% url 'post-detail' pk=post.pk %}">
{% csrf_token %}
{{ comment_form }}
</form>
{% endfor %}
here we thus make a POST request to the post-detail view.
After 4 hours of searching for answers, this is how I achieve it. All I did was add this new view, method, url and html. Hope this helps!
views.py
def imagedetail(request, pk):
post = get_object_or_404(Post, pk=pk)
comments = Comment.objects.filter(post=post)
if request.method == 'POST':
comment_form = CommentForm(request.POST or None)
if comment_form.is_valid():
content = request.POST.get('content')
comment = Comment.objects.create(post=post, user=request.user, content=content)
comment.save()
return HttpResponseRedirect(post.get_absolute_url())
else:
comment_form = CommentForm()
context2 = {
"comments": comments,
"comment_form": comment_form,
}
return render(request, 'imagedetail.html', context2)
models.py (on Post model)
def get_absolute_url(self):
return reverse('imagedetail', args=[self.id])
urls.py (for new view which is imagedetail)
path('imagedetail/<int:pk>/', views.imagedetail, name='imagedetail'),
imagelist.html (to redirect to imagedetail, btw it is a bootstrap button)
<a role="button" class="btn btn-primary" href="{% url 'imagedetail' pk=image.pk %}"></a>
imagedetail.html (it only shows comments)
<form method="post">
{% csrf_token %}
{{comment_form.as_p}}
{% if request.user.is_authenticated %}
<input type="submit" value="Submit" class="btn btn-outline-succes">
{% else %}
<input type="submit" value="Submit" class="btn btn-outline-succes" disabled>
{% endif %}
</form>
<div class="main-comment-section">
{{ comments.count }}
{% for comment in comments %}
<blockquote class="blockquote">
<p class="mb-0">{{ comment.content }}</p>
<footer class="blockquote-footer">by <cite title="Source Title">{{ comment.user }}</cite></footer>
</blockquote>
{% endfor %}
</div>
2 - forms.py icine elave ele:
from django import forms
from .models import Comment
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('name', 'body')
# overriding default form setting and adding bootstrap class
def __init__(self, *args, **kwargs):
super(CommentForm, self).__init__(*args, **kwargs)
self.fields['name'].widget.attrs = {'placeholder': 'Enter name', 'class': 'form-control'}
self.fields['body'].widget.attrs = {'placeholder': 'Comment here...', 'class': 'form-control', 'rows': '5'}
3 - models.py elave ele :
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name="comments")
name = models.CharField(max_length=50)
parent = models.ForeignKey("self", null=True, blank=True, on_delete=models.CASCADE)
body = models.TextField()
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
active = models.BooleanField(default=True)
class Meta:
ordering = ('created',)
def __str__(self):
return self.name
def get_comments(self):
return Comment.objects.filter(parent=self).filter(active=True)
4 - views.py post bele gorunmelidi :
def post (request,slug):
post = Post.objects.get(slug = slug)
latest = Post.objects.order_by('-timestamp')[:3]
comments = post.comments.filter(active=True)
new_comment = None
comment_form = CommentForm()
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
new_comment = comment_form.save(commit=False)
new_comment.post = post
new_comment.save()
comment_form = CommentForm()
context = {
'post': post,
'latest': latest,
'comments': comments,
'comment_form': comment_form
}
return render(request, 'post.html', context)
5 - post.html de author altina divider den sonra elave ele :
<hr/>
<h3>Add Comment</h3>
<form method="post" action="">
{% csrf_token %}
{{ comment_form.as_p }}
<button type="submit" class="btn btn-primary">Comment</button>
</form>
{% with comments.count as total_comments %}
<h3 class="mt-5">
{{ total_comments }} comment{{ total_comments|pluralize }}
</h3>
{% endwith %}
{% if not post.comments.all %}
No comments yet
{% else %}
{% for comment in post.get_comments %}
{% include 'comment.html' with comment=comment %}
{% endfor %}
{% endif %}
</div>
6 - admin.py elave ele :
#admin.register(Comment)
class CommentAdmin(admin.ModelAdmin):
list_display=('name', 'post', 'created', 'active')
list_filter = ('active', 'created', 'updated')
search_fields = ('name', 'body')
7 - templates de comment.html yarat ve icine elave et :
<div class="border-0 border-start border-2 ps-2" id="{{comment.id}}">
<div class="mt-3">
<strong>{{comment.name}}</strong>
{% if comment.parent.name%} to <strong>{{comment.parent.name}}</strong>{% endif %}
<small class="text-muted">On {{ comment.created.date }}</small>
</div>
<div class="border p-2 rounded">
<p>{{comment.body}}</p>
<button class="btn btn-primary btn-sm" onclick="handleReply({{comment.id}})">Reply</button>
<div id="reply-form-container-{{comment.id}}" style="display:none">
</div>
</div>
{% for comment in comment.get_comments %}
{% include 'comment.html' with comment=comment %}
{% endfor %}
8 - models de post un altina elave ele
def get_comments(self):
return self.comments.filter(parent=None).filter(active=True)

How Do I Display The Category Name and Description?

How do I go about displaying the category name and short description from the category model onto the categories.html page? I ask this because I can't figure it out and I go the code from the internet so a little unclear how it works. So the code right now takes you to a page like example.com/cat-slug and then shows all the post under that category but how do I display the current category's name on that page (categories.html) as along with the category description too?
models.py:
class Category(models.Model):
name = models.CharField(max_length=100)
short_desc = models.CharField(max_length=160)
slug = models.SlugField()
parent = models.ForeignKey('self',blank=True, null=True ,related_name='children', on_delete=models.CASCADE)
class Meta:
unique_together = ('slug', 'parent',)
verbose_name_plural = "categories"
def __str__(self):
full_path = [self.name]
k = self.parent
while k is not None:
full_path.append(k.name)
k = k.parent
return ' -> '.join(full_path[::-1])
class Post(models.Model):
user = models.CharField(max_length=50, default=1)
title = models.CharField(max_length=120)
short_desc = models.CharField(max_length=50)
category = models.ForeignKey('Category', null=True, blank=True, on_delete=models.CASCADE)
tags = TaggableManager()
content = models.CharField(max_length=1000)
publish = models.DateField(auto_now=False, auto_now_add=False,)
slug = models.SlugField(unique=True)
def __str__(self):
return self.title
def get_cat_list(self):
k = self.category
breadcrumb = ["dummy"]
while k is not None:
breadcrumb.append(k.slug)
k = k.parent
for i in range(len(breadcrumb)-1):
breadcrumb[i] = '/'.join(breadcrumb[-1:i-1:-1])
return breadcrumb[-1:0:-1]
views.py:
def show_category(request, hierarchy=None):
category_slug = hierarchy.split('/')
category_queryset = list(Category.objects.all())
all_slugs = [ x.slug for x in category_queryset ]
parent = None
for slug in category_slug:
if slug in all_slugs:
parent = get_object_or_404(Category, slug=slug, parent=parent)
else:
post = get_object_or_404(Post, slug=slug)
post_related = post.tags.similar_objects()
comments = post.comments.filter(active=True)
instance = get_object_or_404(Post, slug=slug)
breadcrumbs_link = instance.get_cat_list()
category_name = [' '.join(i.split('/')[-1].split('-')) for i in breadcrumbs_link]
breadcrumbs = zip(breadcrumbs_link, category_name)
if request.method == 'POST':
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
new_comment = comment_form.save(commit=False)
new_comment.post = post
new_comment.save()
else:
comment_form = CommentForm()
context = {
'post': post,
'post_related': post_related,
'comments': comments,
'comment_form': comment_form,
'instance': instance,
'breadcrumbs': breadcrumbs,
}
return render(request, "blog/post_detail.html", context)
context = {
'post_set': parent.post_set.all(),
'sub_categories': parent.children.all(),
}
return render(request, "blog/categories.html", context)
categories.html:
{% extends 'base.html' %}
{% block content %}
Viewing: {{ name }} Category
{{ short_desc }}
<div class="container">
<div class="row">
{% if post_set %}
{% for post in post_set %}
<div class="col-lg-8 col-md-10 mx-auto">
<div class="post-preview">
<a href="/blog/{{ post.slug }}">
<h2 class="post-title">
{{ post.title }}
</h2>
<h3 class="post-subtitle">
{{ post.short_desc }}
</h3>
</a>
<p class="post-meta">Posted by
{{ post.user }} at {{ post.publish }} in {{ post.category }}
<br />
{% for tag in post.tags.all %}
<font class="trigger blue lighten-4" size="3rem">{{ tag.name }}</font>
{% endfor %}
</p>
</div>
<hr>
</div>
{% endfor %}
{% else %}
<p>No post found in this category.</p>
{% endif %}
</div>
</div>
{% endblock %}
I think the implementation of the parent is not right, because in each iteration of loop, the parent will be updated.
for slug in category_slug:
if slug in all_slugs:
parent = get_object_or_404(Category, slug=slug, parent=parent) # <-- with each iteration, value of parent will be updated.
Rather than that, you can define the parent like this:
parent = Category.objects.filter(slug__in=category_slug, parent=None).first()
context = {
'category': parent, # send the category explicitly through the context
'post_set': parent.post_set.all(),
'sub_categories': parent.children.all(),
}
And update the template:
Viewing: {{ category.name }} Category
{{ category.short_desc }}

How can I display the db values of a row in a form before I update the form?

I'm trying to display the employee data after filtering the DB by pk which I passed via the URL.
I can update the form, although I don't want the form fields to be empty since I just want to update, which means its not all the fields I'm going to touch
forms.py
class AddEmployeeForm(forms.Form):
genderset = [(0,'--Select an Option--'),('Male','Male'), ('Female', 'Female')]
marital_status_set = [(0,'--Select an Option--'),('Married','Married'), ('Single', 'Single')]
employment_type_set = [(0,'--Select an Option--'),('Contract','Contract'), ('Full-Time', 'Full-Time'),('Intern', 'Intern')]
employment_status_set = [(0,'--Select an Option--'),('Active','Active'), ('Inactive', 'Inactive')]
first_name = forms.CharField(label = "First Name ", max_length = 200)
last_name = forms.CharField(label = "Last Name ", max_length = 200)
employee_id = forms.IntegerField()
email = forms.EmailField(label = "Email ", max_length = 200)
address = forms.CharField(label = "Address", max_length = 200)
role = forms.CharField( max_length = 200)
date_of_birth = forms.DateField()
join_date = forms.DateField()
end_date = forms.DateField()
location = forms.CharField( max_length = 200)
hod = forms.ModelChoiceField(queryset=Department.objects.only('lead'))
phone_number = forms.CharField( max_length = 200)
employment_type = forms.ChoiceField( choices = employment_type_set)
employment_status = forms.ChoiceField( choices = employment_status_set )
marital_status = forms.ChoiceField( choices = marital_status_set )
gender = forms.ChoiceField( choices = genderset )
department = forms.ModelChoiceField(queryset=Department.objects.only('dept_name'))
credentials = forms.FileField()
passport = forms.FileField()
date_added = forms.DateTimeField( initial = datetime.now, widget=forms.HiddenInput())
views.py
#login_required(login_url='/accounts/login')
def edit(request, pk):
employee = Employee.objects.filter(pk=pk)
form = AddEmployeeForm()
context = {
'employee': employee,
'form':form
}
return render(request, 'employees/edit.html', context)
edit.html
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<div class="page-wrapper">
<div class="content container-fluid">
<div class="row">
<div class="col-xs-4">
<h4 class="page-title">Edit Employee Details</h4>
</div>
<div class="col-xs-4 text-center">
{% include "partials/_alerts.html" %}
</div>
</div>
<form class="m-b-30" action="{% url 'add' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="row"></div>
{% for field in form %}
<div class="col-sm-6">
<div class="form-group">
{{ field.errors }}
{{ field|as_crispy_field }}
{% if field.help_text %}
<p class="help">{{ field.help_text|safe }}</p>
{% endif %}
</div>
</div>
{% endfor %}
</div>
<div class="m-t-20 text-center">
<button class="btn btn-primary">Save Changes</button>
</div>
</form>
</div>
</div>
{% endblock content %}
Its meant to display the values of an employee that was filterd from the database using the PK value
In views.py, you can pass a dictionary into AddEmployeeForm constructor to display the values:
#login_required(login_url='/accounts/login')
def edit(request, pk):
employee = Employee.objects.filter(pk=pk)
field_values = { 'first_name': employee.first_name } #...other fields
form = AddEmployeeForm(field_values)
context = {
'employee': employee,
'form':form
}
return render(request, 'employees/edit.html', context)
A Form instance is either bound to a set of data, or unbound.
If it’s bound to a set of data, it’s capable of validating that data and rendering the form as HTML with the data displayed in the
HTML.
If it’s unbound, it cannot do validation (because there’s no data to validate!), but it can still render the blank form as HTML.
Reference https://docs.djangoproject.com/en/2.2/ref/forms/api/#ref-forms-api-bound-unbound
You can create a form instance and use this format:
form = In_Form(initial={'username': name})
And in your template you can then populate your form using :
{{employee.name}}

The view students.views.addgrregister didn't return an HttpResponse object. It returned None instead

ValueError at /students/addgrregister/
i am trying to add students in gr_register but its giving an error due to this error the code is not working i also upload the template (addgrregister.html) kndly tell me where is the issue in these pages
models.py
class gr_register(models.Model):
Gender_Choices = (
('M', 'Male'),
('FM', 'Female'),
)
Status_Choices = (
('P', 'Present'),
('FM', 'Left'),
)
gr_no = models.IntegerField(primary_key=True)
first_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
date_birth = models.DateField(null=True)
classes_A = models.ForeignKey(Classes, on_delete=models.CASCADE, related_name="classes_A", default=1, verbose_name="Class of Admission")
sections_A = models.ForeignKey(Sections, on_delete=models.CASCADE, related_name="sections_A", default=1, verbose_name="Section of Admission")
gender = models.CharField(max_length=10, choices=Gender_Choices)
classes_C = models.ForeignKey(Classes, on_delete=models.CASCADE, related_name="classes_C", verbose_name="Current Class")
sections_C = models.ForeignKey(Sections, on_delete=models.CASCADE, related_name="sections_C", verbose_name="Current Section")
address = models.CharField(max_length=100, null=True, verbose_name="Home Address")
area_code = models.ForeignKey(Area, on_delete=models.CASCADE, verbose_name="Area")
status = models.CharField(max_length=10, choices=Status_Choices, default='P')
class Meta:
ordering = ('gr_no',)
def __str__(self):
return self.first_name
views.py
from django.shortcuts import get_object_or_404, render, redirect
def addgrregister(request):
if request.method == 'POST':
form = gr_registerForm(request.POST)
if form.is_valid():
form.save()
return redirect('home')
else:
form = gr_registerForm()
return render(request, 'students/addgrregister.html', {'form': form})
forms.py
from django import forms
from django.forms import ModelChoiceField, ModelForm
from .models import *
class gr_registerForm(ModelForm):
classes_A = forms.ModelChoiceField(queryset=Classes.objects.all())
sections_A = forms.ModelChoiceField(queryset=Sections.objects.all())
classes_C = forms.ModelChoiceField(queryset=Classes.objects.all())
sections_C = forms.ModelChoiceField(queryset=Sections.objects.all())
area_code = forms.ModelChoiceField(queryset=Area.objects.all())
class Meta:
model = gr_register
fields = '__all__'
def init(self, *args, **kwargs):
forms.ModelForm.init(self, *args, **kwargs)
addgrregister.html
{% extends 'authenticate/base.html' %}
{% block content %}
<div class="container">
<h4 class="text-center">ADD GR_REGISTER</h4>
<hr/>
<form method="POST" action="{% url 'addgrregister' %}" enctype="multipart/form-data">
{% csrf_token %}
{% for field in form %}
<div class="form-group row">
<label for="id_{{ field.name }}" class="col-2 col-form-label">{{ field.label }}</label>
<div class="col-10">
{{ field }}
</div>
</div>
{% endfor %}
<button type="submit" class="btn btn-primary" name="button">Add GR_REGISTER</button>
</form>
<br/><br/>
</div>
{% endblock %}
There is nothing returned when form is not valid. I think you try like this:
def addgrregister(request):
form = gr_registerForm(request.POST or None) # it initates a form. If the request type is POST, then there will be a dict available with posted data in request.POST. If request is not POST, then the form will initiate with empty data.
if request.method == 'POST':
if form.is_valid(): # Form valid checks if the submitted form is valid or not. If not, it will store errors in the form. When that form is passed to template, it will show errors in html
form.save() # It will store data in DB
return redirect('home')
# when for is invalid, it will show the error in the form
return render(request, 'students/addgrregister.html', {'form': form})
Update
Show form errors in template:
{% for field in form %}
<div class="form-group row">
<label for="id_{{ field.name }}" class="col-2 col-form-label">{{ field.label }}</label>
<div class="col-10">
{{ field }}
{{ field.errors }} // <-- Updated here
</div>
</div>
{% endfor %}

How do I search a database and print results?

What I'm looking to do is make it to where I can search the database directly without having the entire database visible on search page. The search page should be empty. You do a search and it goes straight to the detail page of the db entry which matches your search criteria. If there is no match it shows "No matches found". How can I do this?
This is what I have so far. This prints/lists every entry in the database on the same page as my search box and when a search is done the list gets cut down to however many entries match the search criteria. Every db entry has a link you can click on which will take you to a detail page which pulls up that entry's info.
I'm using Django 2.0.
Manage.py
class PostManager(models.Manager):
def active(self, *args, **kwargs):
return super(PostManager, self).filter(draft=False).filter(publish__lte=timezone.now())
def upload_location(instance, filename):
return "%s/%s" %(instance.slug, filename)
class Remote(models.Model):
brand = models.CharField(max_length=20)
model = models.CharField(max_length=25)
type = models.CharField(max_length=50)
orig = models.TextField()
comp = models.TextField(blank=True)
image = models.ImageField(upload_to=upload_location,
null=True, blank=True, default=0,
timestamp = models.DateTimeField(auto_now=False, auto_now_add=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE, default=1)
slug = models.SlugField(unique=True)
objects = PostManager()
def __str__(self):
return self.model
def get_absolute_url(self):
return reverse("mainapp:detail", kwargs={"slug": self.slug})
def create_slug(instance, new_slug=None):
slug = slugify(instance.model) #slugify title
if new_slug is not None:
slug = new_slug
qs = Remote.objects.filter(slug=slug).order_by("-id")
exists = qs.exists()
if exists:
new_slug = "%s-%s" %(slug, qs.first().id)
return create_slug(instance, new_slug=new_slug)
return slug
def pre_save_post_receiver(sender, instance, *args, **kwargs):
if not instance.slug:
instance.slug = create_slug(instance)
pre_save.connect(pre_save_post_receiver, sender=Remote)
Views.py
def detail(request, slug=None):
instance = get_object_or_404(Remote, slug=slug)
share_string = quote_plus(instance.orig)
context={
"model": instance.model,
"instance": instance,
"share_string": share_string,
}
return render(request, 'mainapp/remote_detail.html', context)
def remote_list(request):
today = timezone.now().date()
queryset_posts = Remote.objects.all()
if request.user.is_staff or request_user_is_superuser:
queryset_posts = Remote.objects.all()
query = request.GET.get("q") # search stuff
if query:
queryset_posts = queryset_posts.filter(
Q(model__icontains=query)|
Q(orig__icontains=query)
).distinct() #.distinct() doesn't allow duplicates to show up
paginator = Paginator(queryset_posts,10) # Show 25 contacts per page
page_request_var = "pagina" #this changes "Page name in pagination
page = request.GET.get(page_request_var)
queryset = paginator.get_page(page)
context={
"object_list": queryset,
"title": "List",
"page_request_var": page_request_var,
"today": today,
}
return render(request, 'mainapp/remote_list.html', context)
HTML for Remote_list view
<form class='mb-2' method='GET' action=''>
<input type='text' name='q' placeholder='Search posts' value='{{ request.GET.q }}'/>
<input href='{{ obj.get_absolute_url }}' type='submit' value='Search'/>
</form>
{% for obj in object_list %}
{% if obj.image %}
<img class="card-img-top" src="{{ obj.image.url }}" alt="Image" style="max-width:100px;">
{% endif %}
<h5 class="card-title"><a href='{{ obj.get_absolute_url }}'>{{ obj.model }}</a></h5>
{% if obj.user.get_full_name %}
<p>Author: {{ obj.user.get_full_name }}</p>
{% endif %}
<p class="card-text">{{ obj.orig|truncatechars:120 }}</p>
View
{% endfor %}
HTML for Detail view
{% if instance.image %}
<img src='{{ instance.image.url }}' class='img-responsive' style='max-width:300px;' />
{% endif %}
<h3>{{ instance.brand}} {{ model }}</h3>
{% if instance.user.get_full_name %}
<p>Author: {{ instance.user.get_full_name }}</p>
{% endif %}
<h5>Originally Supplied With: </h5> {{ instance.orig }}
{% if instance.comp %}
<h5>Compatible With: </h5>{{ instance.comp }}
{% endif %}
you need to add some codes to your qs but now it's okey
you need to learn more about filtering query and passing them to your views if your models contains title field you can do {{ your_data.title }} in your template with your_data is the filtered passed query

Categories

Resources