Django - how to delete model item from list - python

I'm currently building an app with Django for the purpose of creating user-story cards (a bit like a Trello board).
On one page I have my cards displayed as a list:
The code for the list is:
<h1>ScrumBuddy Board</h1>
<ul>
{% for card in cards.all %}
<li class="card">{{ card.title }}
</li>
{% endfor %}
</ul>
And the view def for the board is:
def board(request):
cards = Card.objects
context = {'cards': cards}
return render(request, 'scrumbuddy/board.html', context)
I'd like to add a delete link to each card that removes it from this list, preferable with a confirmation dialogue box. Any suggestions on how to do that would be fantastic.
Many thanks.

I'd suggest using Django's generic class-based views, in your case you would probably use ListView and DeleteView.
For a list view, you could use something like this:
# views.py
from django.views.generic import ListView
from scrumbuddy.models import Card
class CardList(ListView):
model = Card
And for the delete view something like this:
# views.py
from django.views.generic.edit import DeleteView
from django.urls import reverse_lazy
from scrumbuddy.models import Card
class CardDelete(DeleteView):
model = Card
success_url = reverse_lazy('card-list')
As far as the confirmation for the delete action goes, from the Django documentation on the DetailView topic:
A view that displays a confirmation page and deletes an existing
object. The given object will only be deleted if the request method is
POST. If this view is fetched via GET, it will display a confirmation
page that should contain a form that POSTs to the same URL.
Also, note that you can pass arguments to the url template tag, e.g.:
<a href="{% url 'card' id=card.id %}">
instead of:
<a href="{% url 'card' %}/{{ card.id }}">
Note: I guessed that the card.id parameter to your card url is named id - you'll have to update this as per your urls.py if the parameter is named differently.

Related

In Django, how would you have an html page that lists all objects in the database with a common attribute?

First off, I'm a rookie on the field so if I miss out any necessary details, please do let me know and I'll update ASAP.
Working with Django framework and SQLite database, I would like to have two html pages that list all items with the same "type" attribute.
Right now models.py looks like this (there are more attributes after this, but that doesn't matter here, I think):
class Articulo(models.Model):
MEDICINAL = 'med'
AUTOCULTIVO = 'cul'
TIPO_PROD = [
(MEDICINAL, 'Medicinal'),
(AUTOCULTIVO, 'Autocultivo'),
]
tipo = models.CharField(
max_length=3,
choices=TIPO_PROD,
default=MEDICINAL,
)
So I'd like for one of the html pages to list all the items with 'med' and another for all the items with 'cul'.
What I have tried is to write something similar to the search function to bring up those items by filtering that attribute, like this:
def medicinal(request):
items = Articulo.objects.filter(tipo__icontains=med)
return render(request, 'medicinales.html', {'articulos': articulos})
However, I'm really not sure how to continue from there.
I also want to add CSS to the list once it's displayed, but for that I will replicate the one I use for the search function, since I want them to retain the same style.
Thank you very much in advance and again, please let me know if I missed out some important information.
You don't need to use two views to do this, you can define one ListView, and then filter the data shown in the list by defining a get_queryset function in your view. Here is a quick example to give you an idea:
urls.py
path(r'list/', ArticuloListView.as_view(), name='articulo-list')
views.py
class ArticuloListView(ListView):
model = Articulo
context_object_name = 'articulos'
template = 'artucilo_list.html' #this path may differ depending on your project structure
def get_queryset(self):
search_term = self.request.GET['q'] #'q' is defined in your search form
return Articulo.objects.filter(tipo=search_term)
search template
...
<form action="{% url 'articulo-list' %}" method='GET'>
<input name="q" type="text" value="{{ request.GET.q }}" placeholder="Search"/>
</form>
...
articulo_list.html template
...
{% for articulo in articulos %}
{{ articulo.tipo }}
{% endfor %}
...
You dont have to filter based on a search form, you can also manually do it by using the querystring in a link href, like this:
Med List
I have managed to get it done by using a queryset as iri suggested, but in a simpler way.
In urls.py
path('medicinales/', productos_med, name='medicinales'),
In views.py:
def productos_med(request):
queryset = Articulo.objects.filter(tipo='med')
context = {
"object_list": queryset
}
return render(request, "medicinales.html", context)
And in medicinales.html (the page with the list of all 'med' objects):
{% for instance in object_list %}
<p>{{instance.nombre}}</p>
<p>{{instance.descripcion}}</p> <!-- And so on for every wished attribute. -->
Then for objects with attribute 'cul' I followed the same steps changing tipo in queryset to 'cul' and rendering a different html page. After that applying CSS was easy!
Thank you very much iri for answering and pointing me in the right direction and the community for being such an awesome source of info!

Need guidance with FilteredSelectMultiple widget

I am sorry if it question might turn to be little broad, but since I am just learning django (and I am just hobbyist developer) I need some guidance which, I hope, will help someone like me in the future since I could not find any clear and easily comprehensible guide on using this widget. With your answers and help I will try to make this question thread at least guide-ish.
Material I found somewhat helpful for this topic:
Django multi-select widget?
Django: Replacement for the default ManyToMany Widget of Forms
Django's FilteredSelectMultiple widget only works when logged in
Django FilteredSelectMultiple not rendering on page
Use the Django admin app's FilteredSelectMultiple widget in form
Get the chosen values from FilteredSelectMultiple widget in Django
Django FilteredSelectMultiple Right Half Does Not Render
There were few others links, but they did not made anything clearer or added new information so I won't mention them.
Here is what I managed to understand (please correct me if I am wrong or add anything that I missed):
To create FilteredSelectMultiple widget firs I need to amend forms.py (as in any other widget creation process). Amended forms.py should have from django.contrib.admin.widgets import FilteredSelectMultiple import and Media class. forms.py code should look like this (please correct me, because probably it is wrong somewhere):
from django import forms
from catalog.models import DrgCode
from django.contrib.admin.widgets import FilteredSelectMultiple
from django.conf import settings #without it I get an error that settings not defined
class CalculatorForm(forms.Form):
drg_choice = forms.ModelMultipleChoiceField(queryset=DrgCode.objects.all(), widget=FilteredSelectMultiple("Somethings", is_stacked=False), required=True)
class Media:
css = {
'all': (os.path.join(settings.BASE_DIR, '/static/admin/css/widgets.css'),),
}
js = ('/admin/jsi18n',)
Questions about this part:
Am I right about django.conf import? Since I did not see it
imported in any material I found. Answer: during my test I determined that django.conf import is necessary if using settings.BASE_DIR part. In various sources there was two ways of writing css path, this one worked for me.
Do I need to create widgets.css and corresponding directory? Or
django will find it itself? Since there is no such file or directory
generated after I created skeleton-website using django-admin
startproject cmd? Answer: No. There is no need to create widgets.css or any of the files since django finds them itself.
Same question as previous for jsi18n part. Also what is this? I
assume its javascript file, but it has no extension for some reason.
Also I cannot find it anywhere? Should I create it? How to do that?
Or I can copy it from somewhere? Partial answer: no need to create it. Just point at it in urls.py. Still do not know exactly that kind of file it is (or where it is)
After amending forms.py I should ammend urls.pyby adding url(r'^jsi18n/$', 'django.views.i18n.javascript_catalog', name='jsi18n')
So urls.py now look like this:
from django.urls import path
from . import views
from django.conf.urls import url
urlpatterns = [
path('', views.index, name='index'),
'django.views.i18n.javascript_catalog',
name='jsi18n'),
]
Questions:
Am I doing it wright or should I just add it below urlpatterns? Answer: This method if fine.
Now I need to set HTML template file for form to render (like in any other case). Code for it (file named DrgCalculator.html):
{% extends "base_generic.html" %}
<script type="text/javascript" src="{% url 'jsi18n' %}" > </script>
{{ form.media }}
<form enctype="multipart/form-data" method="POST">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="save btn btn-default">Submit</button>
</form>
This part seems more or less clear. But maybe I should amend
something or know about? Answer: Should be changed. Will write full code below.
Lastly I need to adjust views.py to set where this form and everything related with it happens.
From what I understand code in this part is more or less is not directly related with widget, but to complete everything and make working example I will use code I leaned/got in this (very good) django tutorial:
from django.shortcuts import render
from django.http import HttpResponseRedirect
from django.urls import reverse
from catalog.models import DrgCode
from catalog.forms import CalculatorForm
def DrgCalculator(request):
if request.method == 'POST':
form = CalculatorForm(request.POST)
if form.is_valid():
return render(request, 'DrgCalculator.html')
context = {
'form': form,
}
return render(request, 'DrgCalculator.html', context)
Questions:
Any remarks on this part of the code? Answer: Missing else: form = DrgCalculator(). Will write amended code below.
How I will access values which user choose using
FilteredSelectMultiple? I imagine I should clean data in
forms.py like with other widgets. So I should add nested function
below to my class CalculatorForm in forms.py am I right? Answer: Yes, data should be cleaned like in other cases. Function is correct.
def clean_CalculatorForm(self):
drg_choice = self.cleaned_data['drg_choice']
return drg_choice
Data I will get after cleaning will be list or dictionary? Am I
right? Answer: No, from this widget user input received as QuerySet
That is all my questions, sorry for long thread, I tried to make it as clear as possible. If I need to clarify something, please let me know. I will try to edit and update this, to make it friendly for people who will read it in future.
EDIT1: Answered some of my questions.
EDIT2: Answered rest of my questions.
After spending few days of research and testing I managed to get FilteredSelectMultiple widget working outside admin page in user form. As promised in question, I will try to synthesize my accumulated knowledge into some sort of guide which, I hope, will help someone like me in the future. Please note that I am far from professional (just a hobbyist with no computer engineering background to be precise) so my observations might not be exactly correct or the way I done it might not the best one. Despite that it might help you to get on the right track.
So to begin with FilteredSelectMultiple widget first of all - forms.py have to be amended by importing widget, creating field for it (similar like with regular widgets) and adding nested Media class. Code example:
from django.contrib.admin.widgets import FilteredSelectMultiple
class DrgCalculator(forms.Form):
drg_choise = forms.ModelMultipleChoiceField(queryset=DrgCode.objects.all(),
label="Something",
widget=FilteredSelectMultiple("Title", is_stacked=False),
required=True)
class Media:
css = {
'all': ('/static/admin/css/widgets.css',),
}
js = ('/admin/jsi18n',)
def clean_drg_choise(self):
drg_choise = self.cleaned_data['drg_choise']
return drg_choise
As I determined during my test and research class Media should be copied as written and require no changes for widget to work. Files mentioned in this class will be found by django itself so no need to search and copy them (like told in some of the material I read).
After creating form urls.py should be amended. During testing I found out, that in newer django versions (or at lest one I used) javascript_catalog is renamed and url provided cannot be string. So code should look like this:
from django.urls import path
from . import views
from django.conf.urls import url
from django import views as django_views
urlpatterns = [
url(r'^jsi18n/$', django_views.i18n.JavaScriptCatalog.as_view(), name='jsi18n'),
]
Now for the htlm template I am sure that there is more ways of doing it so I just provide example:
{% extends "base_generic.html" %}
{% block content %}
<div id='frame'>
<form action="" method="post">
<div id='sk_body'>
<fieldset>
<legend>Fill required fields</legend>
<form>
{% csrf_token %}
<table>
{{ form.media }}
{{ form.as_table }}
<script type="text/javascript" src="{% url 'jsi18n' %}"></script>
</table>
<input type="submit" value="Count">
</form>
</fieldset>
</div>
</form>
</div>
{% endblock %}
To receive data from this widget fairly standard code in views.py should be used, example code I used:
def DRG_calcualtor(request):
if request.method == 'POST':
form = DrgCalculator(request.POST)
if form.is_valid():
choosen_drg = form.cleaned_data['drg_choise'] #result as QuerySet
choosen_drg_list = list([str(i) for i in choosen_drg]) #you can convert it to list or anything you need
return render(request, 'DRGcalculator_valid.html')
context = {
'form': form,
}
return render(request, 'DRGcalculator.html', context)
else:
form = DrgCalculator()
context = {
'form': form,
}
return render(request, 'DRGcalculator.html', context)

In django, how generic view works

I have started learning django, I'm not sure how generic view works. I read django documentation several times, I can't gain clear understanding of how generic view works. Very strange but it works well. It retrieves data from the database and renders data on the browser.
Here is snippet code of polls/urls.py.
url(r'^$', views.IndexView.as_view(), name = 'index')
It will go to the IndexView class in views.py.
Here is snippet code of polls/views.py.
from django.views import generic
from .models import Question, Choice
class IndexView(generic.ListView):
template_name = 'polls/index.html'
context_object_name = 'latest_question_list'
def get_queryset(self):
return Question.objects.order_by('-pub_date')[:5]
When I change template_name as something, the exception has occurred as follows.
TemplateDoesNotExist: polls/question_list.html
What does question_list.html mean?
Where does it come from?
And here is index.html.
{% if latest_question_list %}
<ul>
{% for question in latest_question_list %}
<li>
{{ question.question_text }}
</li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}
As you can see, index.html file use latest_question_list as a parameter but I'm not sure how can the latest_question_list be used.
In views.py file, what does context_object_name = 'latest_question_list' mean?
Where does the 'latest_question_list' come from and how can index.html use latest_question_list?
Do I use context_object_name in views.py?
What's the role of the get_queryset() function?
What does question_list.html mean?
It means that there is a file inside polls dir like index.html
if you want to have a file with a diff name you have to change the name of template_name='polls/name_of_file_inside_polls'
Do I use context_object_name in views.py?
The context is the place where you put some vars to use it in your templates, that's why you can use latest_question_list
What's the role of the get_queryset() function?
It creates the query that is passed to the template.
I understand what they mean.
By default, the DetailView generic view uses a template called /_detail.html. In my case, it would use the template "polls/question_detail.html". The template_name attribute is used to tell Django to use a specific template name instead of the autogenerated default template name. I also specify the template_name for the results list view – this ensures that the results view and the detail view have a different appearance when rendered, even though they’re both a DetailView behind the scenes.
For DetailView the question variable is provided automatically – since we’re using a Django model (Question), Django is able to determine an appropriate name for the context variable. However, for ListView, the automatically generated context variable is question_list. To override this we provide the context_object_name attribute, specifying that we want to use latest_question_list instead.

Django passing data from one app to another?

I am using django-allauth for my authentication. I have a dashboard which has many links.
user.html
`{% include 'sidebar.html' %}
<h1>Profile View</h1>
<p>{{ profile.username }}</p>`
change_password.html
`{% include 'sidebar.html' %}
<h2>Change Password</h2>
<p>{{ profile.username }}</p>`
sidebar.html
`Profile View
Change Password`
views.py
class ProfileView(DetailView):
template_name = "user.html"
queryset = User.objects.all()
context_object_name = 'profile'
slug_field = "username"
change password view is from django-allauth. How can i pass the the username from the ProfileView to the change password view so that i can display in the change_password.html page.
Main Problem
Since i have been including sidebar.html to both the views, it works good in ProfileView but when i navigate to change_password view i get the following error
Reverse for 'profile_view' with keyword arguments '{'slug': ''}' not found. 1 pattern(s) tried: ['(?P[-\w.#+-]+)/$']
the error does not show in ProfileView because the profile.username returns a value, but when i navigate to change_password view i get the above error because the profile context object only passes in that particular view. i want to use that context_object through out the project.
Is there a simple way to do it? other than build a JSON API?
I just had a quick look at the template tags provided by the allauth app. I haven't tested this (nor do I use the project) but this looks like what you are looking for.
In your template, any template...
{% load account %} {# this should be near the top with any other load tags #}
{# this can be anywhere in the template. I'm not positive what it returns but it looks like it is the username. #}
<p>
{% user_display user %}
</p>
I found the answer here.
I got it working, Thank you #teewuane
sidebar.html
Profile View
replaced as
Profile View

DetailView not showing data

I am trying to display a ListView of items that when clicked opens the respective DetailView for that item. This is a pretty straightforward thing to do in Django, but I'm running into some trouble with it.
I have successfully created a ListView, but I can't get the DetailView to display the information for each item.
I am building a password safe. The ListView lists all the passwords saved, and the DetailView is the view/edit page for each password. And yes, I realize actually putting a password safe on the internet is a terrible idea, it's just a test project :)
My PasswordSafe class:
class PasswordSafe(models.Model):
user = models.OneToOneField(User)
pwWebsite = models.CharField(max_length=30)
pwUsername = models.CharField(max_length=30)
pwEmail = models.CharField(max_length=30)
pwPassword = models.CharField(max_length=30)
def __unicode__(self): # __unicode__ on Python 2, __str__ on Python 3
return self.pwWebsite
User.passwordSafe = property(lambda u: PasswordSafe.objects.get_or_create(user=u)[0])
My urls.py:
from django.conf.urls import patterns, include, url
from django.views.generic import ListView, DetailView
from models import PasswordSafe
urlpatterns = [
url(r'^passwordSafe/$', ListView.as_view(queryset=PasswordSafe.objects.all().order_by('-pwWebsite')[:10], template_name = 'userprofile/passwordSafe_list.html')),
url(r'^passwordSafe/(?P<pk>\d+)/$', DetailView.as_view(model=PasswordSafe, template_name='userprofile/passwordSafe.html')),
]
I use the following code successfully in my ListView template (passwordSafe_list.html):
{% for passwordSafe in object_list %}
<h2>{{ passwordSafe.pwWebsite }}</h2>
{% endfor %}
However, when I try to use the passwordSafe data in my Detailview (the passwordSafe.html template), nothing shows up. I use the following code to try and change the title:
<title>{% block title %} | Edit {{ passwordSafe.pwWebsite }} Password{% endblock %}</title>
This should show a title something like "Password Safe | Edit Google Password", but passwordSafe.pwWebsite returns with nothing. Am I doing something wrong in trying to reference passwordSafe from this template? I know I haven't passed a query set like I did with the ListView, but I'm not sure if that's part of the problem or not.
In the DetailView, Django uses the model name in lowercase, by default, if you haven't set context_object_name.
{{ passwordsafe.pwWebsite }}

Categories

Resources