django Edit records in Listview - django

I have a ListView that lists all questions from the Question model.
the models.py is:
class Question(models.Model):
question_text = models.CharField(max_length=200, unique=True)
pub_date = models.DateField(verbose_name='date published')
def __str__(self):
return self.question_text
now I want users can edit question_text. I tried this in views.py:
class UpdateDirectry(generic.list.ListView, generic.edit.FormMixin):
model = Question
template_name = 'accounts/editable_directory.html'
form_class = forms.EditListForm
def get_context_data(self, *, object_list=None, **kwargs):
context = super(UpdateDirectry, self).get_context_data()
context['object_list'] = Question.objects.filter(question_text__startswith='Who')
return context
and in the template:
<form method="post">
{% csrf_token %}
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Q</th>
<th scope="col">D</th>
</tr>
</thead>
<tbody>
{% for object in object_list %}
<tr>
<th scope="row">{{ forloop.counter }}</th>
<td><input type="text" value="{{ object.question_text }}"></td>
<td>{{ object.pub_date }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<input type="submit" value="Submit">
</form>
I can edit the question_text but when I click submit button nothing happens (just a white page) and no records change in the database.
How can I really edit records with the submit button?
This is what the template shows:
Update 1: ## this is what I exactly want in view (this image is in admin with list_editable):
enter image description here
How can do the exact thing in a view?

Just use a ModelFormSetView from Django Extra Views:
from extra_views import ModelFormSetView
class UpdateDirectry(ModelFormSetView):
model = Question
template_name = 'accounts/editable_directory.html'
form_class = forms.EditListForm
and in your template:
<form method="post">
{{ formset }}
<input type="submit" value="Submit" />
</form>

You have to import UpdateView from :
from django.views.generic.edit import UpdateView
from django.views.generic.edit import FormMixin
from .forms import EditListForm
class UpdateDirectry(UpdateView, FormMixin):
model = Question
template_name = 'accounts/editable_directory.html'
form_class = EditListForm

Related

Not able to display the category name with number of articles in Django

I am trying to show number of articles in each category in my django project. But it shows category id instead of category_name. I want to display category_name and the corresponding number of articles.
blog/views.py
def searchView(request):
statistics = Post.objects.values('cid').annotate(num_articles = Count('cid')).order_by()
return render(request, 'blog/search.html', {'statistics': statistics})
blog/search.html -> here stat.cid shows the category id but I want to show category_name here.
{% extends 'users/base.html' %}
{% block content %}
<div class="container">
<br>
<div class="row text-center">
<div class="col-md-3"> </div>
<div class="col-md-6">
<h4 class="p-2 mb-2 bg-secondary text-white">POPULAR CATEGORIES!!</h4>
<table id="myTable" class="table table-bordered table-hover table-striped shadow-sm bg-white rounded">
<thead>
<tr>
<th>Category</th>
<th>Articles Available</th>
</tr>
</thead>
<tbody>
{% for stat in statistics %}
<tr>
<td>
{{ stat.cid }}
</td>
<td>
{{ stat.num_articles }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock content %}
blog/models.py
from django.db import models
from django.utils import timezone
from django.contrib.auth import get_user_model
from django.urls import reverse
from ckeditor.fields import RichTextField
# Create your models here.
class Category(models.Model):
cid = models.AutoField(primary_key=True)
category_name = models.CharField(max_length=100)
def __str__(self):
return self.category_name
class Post(models.Model):
aid = models.AutoField(primary_key=True)
image = models.ImageField(default='blog-default.png', upload_to='images/')
title = models.CharField(max_length=200)
content = RichTextField()
created = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
cid = models.ForeignKey(Category, on_delete=models.CASCADE, verbose_name='specialization')
approved = models.BooleanField('Approved', default=False)
like = models.ManyToManyField(get_user_model(), related_name='likes', blank=True)
def __str__(self):
return self.title
Post.objects.values('cid') would only give you the pk of the Category. To access the category_name of the Category you should also include that in the values():
# Note the double underscore between cid and category_name
`Post.objects.values('cid', 'cid__category_name')`
Now that the category_name is available, access it in the template like this:
{{ stat.cid__category_name }}
While this is specific to your case, a better answer is here:
https://stackoverflow.com/a/27181936/10951070
I would be going at this from the opposite direction, meaning I would be accessing this data from Category rather than from Post which for some reason you call statistics.
First off I'd suggest you to use a ListView and then I'd proceed as follows:
# views
from django.views.generic import ListView
class CategoryListView(ListView):
model = Category
template_name = 'blog/search.html'
context_object_name = "categories"
# template
<table>
<thead>
...
</thead>
<tbody>
{% for category in categories %}
<tr>
<td>{{ category.cid }}</td>
<td>{{ category.post_set.count }}</td>
</tr>
{% endfor %}
</tbody>
</table>
If you add a related_name in your Post model in the cid attribute and call it posts, you can then in the template write category.posts.count which is more readable. This is just a personal preference and definitely not a rule.

How to update a single item in a list from the template in Django?

I am looking for the best way to make an updateable list from model objects in Django.
Let's say that I have a models.py:
class Foo(models.Model):
bar = models.CharField(max_length=10, blank=True)
foo = models.CharField(max_length=30, blank=True)
and I have a views.py that shows the objects from that model:
def my_view(request):
person_list = Person.objects.all()
ctx= {'person_list': person_list}
return render(request, 'my_app/my_view.html', ctx)
Then I have a template, my_view.html:
...
<table>
<tr>
<th>bar</th>
<th>foo</th>
<th></th>
</tr>
{% for item in person_list %}
<tr>
<td>{{item.bar}}</td>
<td>{{item.foo}}</td>
<td style="width: 5%;"><button type="submit" name="button">Change</button></td>
</tr>
{% endfor %}
</table>
...
So, I would like to add a form and make one of those fields changeable from within this template.
I would like users to be able to change item.foo and then click the change button and it sends the update to the model.
I tried making it a form, and using forms.py to create a form where users can put an input, and then submit the form, and that looked like this, my_view.html:
...
...
<table>
<tr>
<th>bar</th>
<th>foo</th>
<th></th>
</tr>
{% for item in person_list %}
<form method="post">
{% csrf_token %}
<tr>
<td>{{item.bar}}</td>
<td>{{item.foo}}</td>
<td>{{form.foo}}</td>
<td style="width: 5%;"><button type="submit" name="button">Change</button></td>
</tr>
</form>
{% endfor %}
</table>
...
...
And that was not working, because I couldn't figure out where to send the PK for that particular item in the Model.
Any help is appreciated.
You can do it using class-based views very easily
Say this is your models.py
class MyModel(models.Model):
alpha = models.CharField(max_length=200)
beta = models.CharField(max_length=200)
gama = models.CharField(max_length=200)
delta = models.CharField(max_length=200)
Your views.py should be like this
from django.views.generic import UpdateView
from .models import MyModel
class MyModelUpdateView(UpdateView):
model = MyModel
fields = ['beta'] # Include the fields you want to update form your template
As a reference, this will be your CreateView in views.py file
class MyModelCreateView(CreateView):
model = MyModel
template_name = 'myapp/myapp_form.html'
fields = ['alpha', 'beta', 'gama', 'delta']
This will be your urls.py
from .views import MyModelUpdateView
urlpatterns = [
....
path('app/<int:pk>/update/', MyModelUpdateView.as_view(), name='model-update'),
....
]
The default template for UpdateView is the one used for CreateView, myapp_form.html in this case. This will be your basic form template
{% extends 'base.html' %}
{% block content %}
<form method="POST">
{% csrf_token %}
{{ form }}
<button type="submit">Create</button>
</form>
{% endblock %}

Django class based view to query database from form and display results

So I am completely new to Django, I want to have a user enter a keyword into an HTML form then have each row from the database where an attribute matches that keyword displayed on the page. I've tried various ways of doing this and am not sure what I am doing wrong. Any help would be appreciated.
search.html
<div class="container">
<form method="GET" action="{% url 'search' %}">
<div class="form-group">
<input type="text" name="make" placeholder="Car Make" />
<label>
<button type="submit" class="btn btn-danger"> Go </button>
</label>
</div>
</form>
{% if results %}
<table>
<tr>
<th scope="col"></th>
<th scope="col">Car Make</th>
<th scope="col">Car Model</th>
<th scope="col">Car Type</th>
<th scope="col">Number of Seats</th>
<th scope="col">Price</th>
</tr>
{% for item in results%}
<tr>
<td>{{item.makename}}</td>
<td>{{item.model}}</td>
<td>{{item.seriesname}}</td>
<td>{{item.seatingcapacity}}</td>
<td>{{item.pricenew}}</td>
</tr>
{% endfor %}
</table>
{% endif %}
</div>
views.py
class SearchView(TemplateView):
template_name = 'carproject/search.html'
model = Vehicles
def get(self, request):
form = AdvancedSearch()
return render(request, self.template_name, {'form': form})
def search(self, request):
makequery = self.request.GET.get['make']
if makequery:
results = self.Vehicles.objects.filter(makename__icontains(makequery))
return render(request, self.template_name, {'results': results})
Models.py
class Vehicles(models.Model):
carid = models.IntegerField(db_column='CarID', primary_key=True)
makename = models.CharField(db_column='MakeName', max_length=45)
model = models.CharField(db_column='Model', max_length=45)
seriesname = models.CharField(db_column='SeriesName', max_length=45)
seriesyear = models.TextField(db_column='SeriesYear')
pricenew = models.IntegerField(db_column='PriceNew')
fuelsystem = models.CharField(db_column='FuelSystem', max_length=45)
enginesize = models.CharField(db_column='EngineSize', max_length=10)
tankcapacity = models.CharField(db_column='TankCapacity', max_length=10)
power = models.CharField(db_column='Power', max_length=10)
seatingcapacity = models.IntegerField(db_column='SeatingCapacity')
standardtransmission = models.CharField(db_column='StandardTransmission', max_length=45)
bodytype = models.CharField(db_column='BodyType', max_length=45)
drive = models.CharField(db_column='Drive', max_length=3)
wheelbase = models.CharField(db_column='WheelBase', max_length=10)
class Meta:
managed = False
db_table = 'vehicles'
You can just do Vehicles.objects.filter(makename__icontains=request.GET.get("make","somevalueasdefault")) in your get function. Maybe I am missing something, but I am not sure why you have rendered the view like that in a class-based view. Just as an example, you can do like below.
class SearchView(TemplateView):
template_name = "carproject/search.html"
def get(self, kwargs):
context = super(SearchView, self).get_context_data(**kwargs)
context['queryset'] = Vehicles.objects.filter(makename__icontains=request.GET.get("make","sdefault"))
return context

author = models.ForeignKey('auth.User', null=True, blank=False) trouble

I have a table where I save data(description, x, y, result and creation date) and until now everything works.
I thought then to add a column with the author for each saved line eg:
DES| X | Y | RESULT |CREATION DATE| AUTHOR |
hi | 3| 1 | 4 | 24/02/2015 | username |
then I added in models.py auth:
from django.db import models
from django.utils import timezone
from simpleapp.oper import add_divide
from django.conf import settings
class ElementiTab(models.Model):
author = models.ForeignKey('auth.User', null=True, blank=False)
des = models.CharField(max_length=30)
x = models.FloatField()
y = models.FloatField()
res = models.FloatField(default=0)
created_date = models.DateTimeField(default=timezone.now)
def save(self, *args, **kwargs):
self.res = add_divide(self.x, self.y)
super(ElementiTab, self).save(*args, **kwargs)
def __str__(self):
return self.des
UPDATE:
forms.py
from django import forms
from .models import ElementiTab
class ElementiTabForm(forms.ModelForm):
class Meta:
model = ElementiTab
fields = ('des', 'x', 'y')
views.py
#login_required
def tabval(request):
# if this is a POST request we need to process the form data
valori = ElementiTab.objects.filter().order_by('-created_date')
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = ElementiTabForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
form.save()
# if a GET (or any other method) we'll create a blank form
else:
form = ElementiTabForm()
return render(request, 'simpleapp/simpleapp.html', {'form': form, 'valori': valori})
#user_passes_test(lambda u: u.is_superuser)
def delete(request, id):
valori_to_delete = get_object_or_404(ElementiTab, pk=id).delete()
return redirect(tabval)
simpleapp.html
{% extends 'registration/base_reg.html' %}
{% block title %}SimpleApp-tabval{% endblock %}
{%block content%}
<h4>TABELLA CON DATI</h4>
<form action="/simpleapp/" method="post">
{% csrf_token %}
{{ form.as_table }}
<input type="submit" value="LIST" />
</form>
<form action="/simpleapp/" method="DELETE">
{% csrf_token %}
<input type="submit" name="canc" value="RESET" />
</form>
<br />
<br />
<div class="table-responsive">
<table class="table table-bordered">
<tr class="info">
<td width="15%" align="center"> NOME</td>
<td width="15%" align="center"> X </td>
<td width="15%" align="center"> Y </td>
<td width="15%" align="center"> RISULTATO </td>
<td width="15%" align="center"> DATA CREAZIONE </td>
<td width="15%" align="center"> AUTORE </td>
{% for elementi in valori %}
<div class="elementi">
<tr>
<td>{{elementi.des}}</td>
<td>{{elementi.x}}</td>
<td>{{elementi.y}}</td>
<td>{{elementi.res}}</td>
<td>{{elementi.created_date}}</td>
<td>{{elementi.author}}</td>
<td width="1%">
{% if user.is_superuser %}
Delete
{% else %}
<span style='font-size: small'>Only Admin</span>
{% endif %}
</td>
</div>
{% endfor %}
</table>
</div>
{% endblock content %}
The fact is that the admin page displays a drop-down menu from which I (as administrator) can choose one of the registered user and so I add them both in the table of my app and in the db.
How can I make this process automatic? I.e. after the login, you put data in the table and once saved the data, also the username is saved and should not be the administrator to set it.
I searched a similar question here but I have not found one to help me to solve my problem.
I updated my answere, i misenderstood your question.
Change this in your view
if form.is_valid():
# Creating the object without commiting to database
obj = form.save(commit=False)
# Setting the user from request
obj.author = request.user
# Commiting to the database
obj.save()

My django python functions not running correctly

I am currently working on an e-commerce web app and decided to use the Beginning Django Ecommerce book. I am following the content and implementing it in my own way but i am having some issues with some few functions that are not running.
here are the apps with the files where i think the problem is coming from;
1. cart app models.py:
from django.db import models
from menu_items.models import Item
from smartmin.models import SmartModel
import django.db.models.options as options
options.DEFAULT_NAMES = options.DEFAULT_NAMES + ('augment_quatity','name','price','get_absolute_url','total',)
class OrderItem(SmartModel):
order_id = models.CharField(max_length=50)
date_added = models.DateTimeField(auto_now_add=True)
quantity = models.IntegerField(default=0)
item = models.ManyToManyField(Item)
class Meta:
db_table='order_items'
def __unicode__(self):
return "%s" % (self.order_id)
def total(self):
return self.quatity *self.item.price
def name(self):
return self.item.name
def price(self):
return self.item.price
def get_absolute_url(self):
return self.item.get_absolute_url()
# incase user orders same item twice we jus add on the quantity
def augment_quatity(self, quantity):
self.quatity = self.quantity + int(quantity)
self.save
orders.py in the same app:
from cart.models import OrderItem
#from cart.models import order_id
from menu_items.models import Item
from django.shortcuts import get_object_or_404
from django.http import HttpResponseRedirect
import decimal
import random
ORDER_ID_SESSION_KEY = 'order_id'
# get the current user's cart id, sets new one if blank
def _order_id(request):
if request.session.get(ORDER_ID_SESSION_KEY,'') == '':
request.session[ORDER_ID_SESSION_KEY] = _generate_cart_id
return request.session[ORDER_ID_SESSION_KEY]
def _generate_cart_id():
order_id =''
characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!##$%^&*()'
order_id_length = 100
for y in range(order_id_length):
order_id += characters[random.randint(0,len(characters)-1
)]
return order_id
# return all items from the current user's order
def get_order_items(request):
return OrderItem.objects.filter(order_id=_order_id(request))
# add an item to order
def add_to_order(request):
postdata = request.POST.copy()
#get item slug from post data, return blank if empty
# item_slug = postdata.get('item_slug','')
#get quantity added, return 1 if empty
quantity = postdata.get('quantity',1)
# fetch the item or return missing page error_message
i = get_object_or_404(Item,)
# get items in order
order_items = get_order_items(request)
item_in_orders = False
# check to see if item is already in cart
for order_item in order_items:
if order_item.item.id == i.id:
#update the quantity if found
order_item.augment_quantity(quantity)
item_in_order = True
if not item_in_order:
# creat and save a new order item
oi = OrderItem()
oi.item = i
oi.quantity = quantity
oi.order_id = _order_id(request)
oi.save()
2.live app views.py
def show_order(request):
if request.method == 'POST':
postdata = request.POST.copy()
if postdata['submit'] == 'Remove':
order.remove_from_order(request)
if postdata['submit'] == 'Update':
order.update_order(request)
order_items = order.get_order_items(request)
page_title = 'F4L order'
order_subtotal = order.order_subtotal(request)
return render_to_response('public/order.html',context_instance=RequestContext(request))
Template where the functionality is not working,
{% extends "base.html" %}
{% block content %}
{% load menu_tags %}
<div style="height:30px">
{% order_box request %}
</div>
<table summary="Your menu order" id="menu_order">
<caption>Your F4L Orders</caption>
<thead>
<tr>
<th scope="col">Item</th>
<th scope="col">Price</th>
<th scope="col" class="right">Total</th>
</tr>
</thead>
<tfoot>
<tr>
<th class="right" colspan="2">
Order Subtotal:
</th>
<th class="right">
{{order_subtotal}}<span> frw</span>
</th>
</tr>
{% if order_items %}
<tr>
<th class="right" colspan="2">
Checkout Now
</th>
</tr>
{% endif %}
</tfoot>
<tbody>
{% if order_items %}
{% for item in order_items %}
<tr>
<td>
{{ item.name }}
</td>
<td>{{ item.price }}<span> frw</span></td>
<td class="right">
<form method="post" action="." class="order">
<label for="quantity">Quantity:</label>
<input type="text" name="quantity" value="{{ item.quantity }}" id="quantity" size="2" class="quantity" max_length="5" />
<input type="hidden" name="item_id" value="{{ item.id }}" />
</td>
<td>
<input type="submit" name="submit" value="update"/>
</form>
</td>
<td>
<form method="post" action="." class="order">
<input type="hidden" name="item_id" value="{{ item.id }}" />
</form>
</td>
<td>
<form method="post" action="." class="order">
<input type="hidden" name="item_id" value="{{ item.id}}" />
<input type="submit" name="submit" value="Remove" />
</form>
</td>
<td class="right">{{ item.total }}<span> frw</span></td>
</tr>
{% endfor %}
{% else %}
<tr>
<td colspan="2" style="height:30px;">
Your F4L order is empty.
</td>
</tr>
{% endif %}
</tbody>
</table>
{% endblock %}
Now th problem is, the above template code is a page a user redirects to after submitting a form with the quantity of item he/she is buying but that is not happening. After submitting form with for example 10items,i redirect to this page(above _template_), it loads correctly but does not return the information i submitted.
i do understand that this is alot but i have really need your help and will appreciate any sort of help.
In show_order view you should pass your variables to template as dictionary:
...
context_dict = {'order_items': order_items, 'order_subtotal': order_subtotal}
return render_to_response('public/order.html', context_dict, context_instance=RequestContext(request))