Django let User Update TextField value on Site via forms? - python

I hope someone can give me a little help on how to do the following with Django (excuse me if I don't explain everything correct, still new to Django and don't know about a lot of things) :
I have a table of Movies, those Movies have a "Description" Datafield, where when they click on it a form opens up with the current description of the movie. If they double click on this description they are allowed to change it and then save the value. I've made a small gif to visualize the idea:
At least thats the basic Idea behind this, so far I've managed to make most of the things run, but sadly not the Django part where the "new" data from the user is send to the Databank and replaces the old data of the Description.
So could someone explain to me how I can make that work? I know that I'd probably have to write a function to my views.py and then create a new url pattern, but I just can't figure out how exactly. So any help is welcome! Below is my code (I hope I've included every file you guys need):
views.py
from django.shortcuts import get_object_or_404, render
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.views import generic
from django.views.generic.list import ListView
from .models import *
class AllMovies(generic.ListView):
model = Movie
template_name = "consilium/index.html"
context_object_name = "latest_movie_list"
class MovieDetails(generic.DetailView):
model = Movie
template_name = "consilium/detail.html"
urls.py
from django.conf.urls import url
from . import views
from .models import *
from django.views.generic.list import ListView
app_name = "consilium"
urlpatterns = [
url(r'^$', views.AllMovies.as_view(), name="index"),
url(r'^(?P<slug>[\w_0-9]+)/$', views.MovieDetails.as_view(), name='detail'),
]
models.py
from django.db import models
from decimal import Decimal
from django import forms
from django.contrib import admin
class Movie(models.Model):
// removed the other models for better overview
description = models.TextField('Movie Description')
def __str__(self):
return self.title
index.html
{% extends "consilium/base.html" %}
{% block body %}
<table class="table">
<thead>
<tr>
<th></th>
<th colspan="2">My Movielist</th>
<th>
</tr>
<tr>
<th></th>
<th>TITLE</th>
<th>GENRE</th>
<th>RELEASE DATE</th>
<th>DIRECTOR</th>
<th>DESCRIPTION</th>
<th>RUNTIME</th>
<th>STATUS</th>
<th>IMDB</th>
</tr>
</thead>
<tbody>
{% if latest_movie_list %}
{% for movie in latest_movie_list %}
<tr>
<td></td>
<td>
<a href="{% url 'consilium:detail' movie.slug %}" data-toggle="popover" data-placement="left" data-content='<img class="title-image" src="{{movie.image.url}}"/>'>{{ movie.title }}</a>
</td>
<td>{{ movie.get_genre_display}}</td>
<td>{{ movie.date}}</td>
<td>{{ movie.director}}</td>
<td id="icn-change" data-toggle="collapse" data-target=".demo{{ forloop.counter }}">
Description <i class="fa fa-caret-right"></i>
</td>
<td>{{ movie.runtime}} min</td>
<td>{{ movie.get_status_display}}</td>
<td>{{ movie.imdb}}</td>
</tr>
<tr>
<td></td>
<td class="hiddenRow" colspan="8">
<div class="container collapse demo{{ forloop.counter }}">
<div class="row justify-content-center">
<div class="col">
<form method="post" id="usrform">{% csrf_token %}
<textarea id="text" class ="form-control" readonly="true" onkeydown="expandtext(this)" ondblclick="this.readOnly='';">{{movie.description}}</textarea>
</form>
</div>
</div>
<div class="row justify-content-center">
<div class="col align-self-start">Double Click to Edit</div>
<div class="col align-self-end">
<input type="submit" id="set" class="pull-right"/>
</div>
</div>
</div>
</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td>No Movies are available.</td>
</tr>
{% endif %}
</tbody>
</table>
{% endblock %}
script.js
// removed all other code for overview
// replace description text with user input
$('#set').click(function() {
var test = $('#text').val();
localStorage.setItem("test", test);
});
$('#text').text(localStorage.getItem("test"));
I hope I didn't miss anything, thanks for everyone who can help me!

I worked on a similar project, and here is what I did.
from django.forms.models import model_to_dict
#login_required
def edit_profile(request):
profile, created = ClientProfile.objects.get_or_create(user_id=request.user.id)
if request.method == 'POST':
form = ProfileSubmissionForm(request.POST, instance=profile)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('jobs:list'))
else:
profile_dict = model_to_dict(profile)
form = ProfileSubmissionForm(profile_dict)
return render(request, 'jobs/profile.html', {'form': form})
Essentially, the model_to_dict renders the values stored in the database in the form. The instance=profile makes sure I'm updating the form and not creating a new object.

Figured it out thanks to the great help of the pythondev slack community!
views.py: getting the description field of my Movie Model
class MovieUpdateForm(forms.ModelForm):
class Meta:
model = Movie
fields = ['description']
reverse_lazy is important, so that when I click on my button it won't redirect me to consilium(my appname)/2/update and stays on the index site where I have my table
class MovieUpdateView(UpdateView):
model = Movie
form_class = MovieUpdateForm
success_url = reverse_lazy('consilium:index')
urls.py:
url(r'^(?P<pk>[0-9]+)/update/$', views.MovieUpdateView.as_view(), name='movie_update'),
here it was important to put this before my slug url pattern I had in my urls.py, otherwise it didn't work:
url(r'^(?P<slug>[\w_0-9]+)/$', views.MovieDetails.as_view(), name='detail'),
My form in my html: using pk=movie.pk so it will grab the correct movie and giving my textarea the name of "description" so my method knows where the data is coming from
<form action="{% url 'consilium:movie_update' pk=movie.pk %}" method="post" id="usrform">{% csrf_token %}
<textarea id="text" class ="form-control" name="description" readonly="true" onkeydown="expandtext(this)" ondblclick="this.readOnly='';">{{movie.description}}</textarea>
<input type="submit" id="set" class="pull-right"/>
</form>

Related

Display join date and last_login in Django

I have User default Django User model to register user. I want to print user last_login and join_date on the template. And also I want to check if user is active or not.
I want to check user active status like this, If user is currently using my website the status should be "Active" If User logged out Then status should be "Loged out"
if it is possible please provide me solution for this as well
accounts/models.py
class Profile(models.Model):
user = models.OneToOneField(User, default="1", on_delete=models.CASCADE)
image = models.ImageField(upload_to="images/", default="default/user.png")
def __str__(self):
return f'{self.user} profile'
dashboard/views.py (to display user data)
def admin_home(request):
data = Profile.objects.filter(Q(user__is_superuser=False), Q(
user__is_staff=False)).order_by('-user__last_login')[:10]
context = {
'data': data,
}
return render(request, "instructor/admin_home.html", context)
Accounts/urls.py
from django.contrib import admin
from . import views
from django.contrib.auth import views as auth_views
from django.urls import path
app_name="accounts"
urlpatterns=[
path('register',views.register,name="register"),
path('admin_login',views.admin_login,name="admin_login"),
path('user_login',views.user_login,name="user_login"),
path('admin_editprofile',views.admin_editprofile, name="admin_editprofile"),
path('user_editprofile',views.user_editprofile, name="user_editprofile"),
path('admin_profile',views.admin_profile, name="admin_profile"),
path('user_profile',views.user_profile, name="user_profile"),
path('Logout',views.Logout, name="Logout"),
path('user_chage_password' , views.user_chage_password , name="user_chage_password"),
path('admin_change_password',views.admin_change_password,name="admin_change_password"),
]
Extra I have two apps one is the dashboard and other accounts. In the accounts app I am implementing only accounts' related URLs and in the dashboard app I am using it for other things.
For user registration, I am using the Django's default User model.
show_all_users.html or template file (for displaying user's data)
<html lang="en" class="no-focus">
<head>
{% load static %}
<link rel="stylesheet" href="{% static 'js/plugins/datatables/dataTables.bootstrap4.min.css' %}">
<link rel="stylesheet" id="css-main" href="{% static 'css1/codebase.min.css' %}">
</head>
<body>
<div id="page-container" class="sidebar-o sidebar-inverse side-scroll page-header-fixed main-content-narrow">
{% include "instructor/sidebar.html" %}
{% include "instructor/admin_nav.html" %}
<!-- Main Container -->
<main id="main-container" style="background-color:white;">
<!-- Page Content -->
<div class="content">
<div class=" container mt-5" style=" position: absolute; margin-left:-4%;">
<div>
<h1 class="text-success text-center"></h1>
<h4 class="text-center text-danger">Recently Logged in users</h4>
<table id="datatable" class="table table-striped table-bordered" style="width:100% color:gray;" >
<thead style="background-color : #607d8b;">
<tr>
<th scope="col">ID</th>
<th scope="col">Profile</th><th scope="col">Username</th>
<th scope="col">Last_login</th>
<th scope="col">join_date</th>
<th scope="col">Action</th>
</tr></thead>
<tbody>
{% for userData in data %}
<tr>
<th scope="row">{{userData.id}}</th>
<td><img style="width: 40px; border-radius:15px;" src="{{userData.image.url}}" alt="this is it."></td>
<td>{{userData.user.username}}</td>
<td> {{request.user.profile.date_joined }}</td>
<td>{{ request.user.profile.last_login }} </td>
<td>
<button class="btn btn-danger btn-sm" onclick="window.mytest()">Delete</button>
<script type="text/javascript">window.mytest = function() { var isValid = confirm('Are you sure to Delete this user?');
if (!isValid) { event.preventDefault(); alert("It wont delete. Yay!");}}</script>
</td>
</tr>
{% endfor %} </tbody></table></div>
</div>
</main>
{% endblock body %}
But this <td> {{request.user.profile.date_joined }}</td> line prints login date of currently logged in user but I want to show all user's last_login date.
And this <td>{{ request.user.profile.last_login }} </td> line prints nothing.
You can filter those users which have logged in at least one time through Q(user__last_login__isnull=False).
Try this query in the view:
views.py
def admin_home(request):
data = Profile.objects.filter(Q(user__is_superuser=False), Q(
user__is_staff=False), Q(user__last_login__isnull=False)).order_by('-user__last_login')[:10]
context = {
'data': data,
}
return render(request, "instructor/admin_home.html", context)
Issue:
Only last_login and date_joined are the valid fields in default User model of django.
Don't use request.user....., since it prints the details of current logged in users, as you stated that I want it to display for all users.
Question 1: But this <td> {{request.user.profile.date_joined }}</td> line prints login date of currently logged in user but I want to show all user's last_login date.
Answer: This is because request... give you current logged in user's details not all users' details.
Question 2: And this <td>{{ request.user.profile.last_login }} </td> line prints nothing.
Answer: It is because, there is wrong relationship of models.
Solution:
Try below code in template file for displaying all users:
<td> {{userData.user.last_login }}</td>
<td>{{ userData.user.date_joined }} </td>
Instead of this code you've tried:
<td> {{request.user.profile.date_joined }}</td>
<td>{{ request.user.profile.last_login }} </td>
Remember: Here, you have filtered only those users which are not staff as well as superuser, so these users are normal users. And if last_login column prints None that means the user has not logged even single time. And for date_joined,by default current time is taken by django. And if you see another time than your local machine that means you haven't set time zone in TIME_ZONE in settings.py. The timezone for India is Asia/kolkata, for more info refer Time zones[django-doc].
create signals.py and add this snippet:
from django.contrib.auth.signals import user_logged_in,user_logged_out
from datetime import datetime
#receiver(user_logged_in)
def user_logged(sender,request,user,**kwargs):
user.profile.last_login = datetime.now()
user.profile.save()
add the related_name attribute to your user field below your Profile model:
class Profile(models.Model):
user = models.OneToOneField(User,default="1", on_delete=models.CASCADE, related_name="profile")
image = models.ImageField(upload_to="images",default="default/user.png")
date_joined = models.DateTimeField()
def save(self, *args, **kwargs) -> None:
self.date_joined = datetime.now()
return super().save(*args, **kwargs)
def __str__(self):
return f'{self.user} profile'
run on terminal:
python manage.py makemigrations
python manage.py migrate
the official documentation

How to download an updated file by admin in Django

what i am trying to do is:
Admin uploads a PDF file from admin panel. (1)
It needs to go to the specified template. (2)
And it should be downloaded by pressing download button in the template.
So here are codes:
(1)
class Reports(models.Model):
name = models.CharField(max_length=100, null=False, blank=False, verbose_name="File Name")
report = models.FileField()
(2)
<tr>
<td>"File Name must be showed in here"</td>
<td class="text-center">PDF</td>
<td class="text-center lang-tr-src"><i class="fas fa-file-download"></i></td>
<td class="text-center lang-en-src"><i class="fas fa-file-download"></i></td>
</tr>
In the website there will be one report for every month. I want to list them in the template and make them downloadable.
Should i write a view for that(if yes how it should be?) or what should i do?
Every single data you want to show to your template you need write it in your views.py, so this case is so specefic.
views.py:
def your_view_name(request):
reports = Reports.objects.all()
context = {
'reports': reports
}
return render(request, 'your_template.html', context)
Then make a url for your view in urls.py
urlpatterns = [
path("", views.your_view_name, name='your_url_name')
]
Your template:
<tr>
{% for obj in reports %}
<td>{{ obj.name }}</td>
<td class="text-center">PDF</td>
<td class="text-center lang-tr-src"><a href="{{ obj.report.url }}" Download
target="_blank"><i class="fas fa-file-download"></i></a></td>
<td class="text-center lang-en-src"><a href="" target="_blank"><i
class="fas fa-file-download"></i></a></td>
{% endfor %}
</tr>
create a new view firstly.
def report_view(request):
context = {}
reports= Reports.objects.all()
context['reports'] = reports
return render(request, "pages/report.html", context)
create an url for this view in urls.py
path('reports', report_view, name='report_view'),
in your template create forloop for this context like below:
{% for report in reports %}
<tr>
<td>"File Name must be showed in here"</td>
<td class="text-center">PDF</td>
<td class="text-center lang-tr-src"><i class="fas fa-file-download"></i></td>
<td class="text-center lang-en-src"><i class="fas fa-file-download"></i></td>
</tr>
{% endfor %}

'module' object is not iterable

My Django project is returning a TypeError: 'module' object is not iterable.
I know this type of question is already being asked in community, but none of previous questions could fixed my issue.
perhaps I don't understand something basic, as I'm a novice who freshly learning Python and Django. does anyone can help me to solve this issue?
I created a model as following.
from django.db import models
# Create your models here.
class Article(models.Model):
content = models.CharField(max_length=200)
written_date = models.DateTimeField('date written')
def __unicode__(self):
return self.content
Following is view.py
# Create your views here.
from blog.models import Article # Article data models
from django.shortcuts import render # shortcuts to call template
from django.http import HttpResponseRedirect # Redirection Module
from django.template import context
from django.utils import timezone # time Module
# blog.views.index
# retrieve all content and display in reverse of order of written_date
# call the template after sorting.
def index(request):
all_articles = Article.objects.all().order_by('-written_date')
return render({'all_articles' : all_articles, 'message' : 'Write something!'},
'blog/index.html', context)
# blog.views.submit
# Receive POST request submitted from user FORM, save the request
# redirect user to index.html
def submit(request):
try:
cont = request.POST['content']
except (KeyError):
return render({'all_articles' : all_articles, 'message' : 'Failed to read content'},
'blog/index.html', context)
else:
article = Article(content=cont, written_date=timezone.now())
article.save()
return HttpResponseRedirect('/blog')
# blog.views.remove
# delete content which has primary key matched to article_id
# redirect user after deleting content
def remove(request, article_id):
article = Article.objects.get(pk=article_id)
article.delete()
return HttpResponseRedirect('/blog')
Index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>One Line Blog</title>
<link rel="stylesheet" href="{{ STATIC_URL }}styles/blog.css" type="text/css">
</head>
<body>
<div id="header">
<h2>One Line Blog</h2>
</div>
<div id="writer">
<div>
{% if message %}<p><strong>{{ message }}</strong></p>{% endif %}
</div>
<form action="/blog/submit" method="post">
{% csrf_token %}
<input type="text" max-length=200 style="width:500px;" name="content">
<input type="submit" value="Submit">
</form>
</div>
<div>
{% if all_articles %}
<table>
<tr>
<th>No. </th>
<th width="300px">Content</th>
<th>Date</th>
<th>Delete</th>
</tr>
{ % for article in all_articles %}
<tr>
<td>{{ article.id }}</td>
<td>{{ article.content }}</td>
<td>{{ article.written_date }}</td>
<td align="center">[x]</td>
</tr>
{ % endfor %}
</table>
{% else %}
<p>No articles available</p>
{% endif %}
</div>
</body>
</html>
The signature of render is:
render(request, template_name, context=None, content_type=None, status=None, using=None)
You, however, call it in your index view:
return render({'all_articles' : all_articles, 'message' : 'Write something!'},
'blog/index.html', context)
where you pass a dict as request (bad enough) and, which causes the error, as third positional argument (which should be a dict) you pass a variable by the name of context which is a module that you have imported via
from django.template import context
Change it to
return render(request, 'blog/index.html',
context={'all_articles': all_articles, 'message': 'Write something!'})
You made the same mistake in your submit view.

Why the django form data cannot display after submitting

After submitting the form (input), it will redirect to another page(result) and show the form information in this result page.
The problem now is that the submitted information doesn't show in the result page.
I doubt the reason is because in result' views.py, I want to show the submitted form from Input,but the form's data is not saved or I shouldn't fetch the data from the model,I should fetch the data from view,so it cannot display the table:
context['input'] = Input.objects.all()
Please correct me if my guess is right or not. Thanks in advance for your suggestion.
input- models.py
class Input(models.Model):
company=models.CharField(max_length=100,default='Empty')
region=models.CharField(max_length=100,default='Empty')
start_date=models.DateField(auto_now=False, auto_now_add=False,default='Empty')
end_date=models.DateField(auto_now=False, auto_now_add=False,default='Empty')
input -- form
class Inputform(forms.ModelForm):
company=forms.CharField()
regionlist = forms.ModelChoiceField(queryset=Result.objects.values('region').distinct())
start_date=forms.DateField(widget=DateInput(),required=True)
end_date = forms.DateField(widget=DateInput(),required=True)
class Meta:
model = Input
fields = ('company', 'region','start_date','end_date')
widgets = {
'start_date': forms.DateInput(attrs={'class':'datepicker'}),
'end_date': forms.DateInput(attrs={'class':'datepicker'}),
}
input -- part of the html code
<form action="result_list/" method="post">{% csrf_token %}
<!--enter company-->
<div class="field">
<p>Company:<input type="text" name="Company" value="{{company}}"/>
</div>
<!--select region-->
<div class="field" >
<label> Select the Region:
{{ form.regionlist }}
{% for region in form.regionlist.choices %}
<option value="{{ val }}" {% ifequal data.val val %}selected {% endifequal %}></option>
{% endfor %}
</label>
</div>
....
<div class="fieldWrapper">
<p><input type="submit" value="Submit" /></p></div>
</form>
input --views
from django.http import HttpResponseRedirect
from django.shortcuts import render,render_to_response,get_object_or_404
from inputform.forms import Inputform
from inputform.models import Input
from result.models import Result
from django.views.decorators.csrf import csrf_exempt
from django.views.generic.list import ListView
#csrf_exempt
def input(request):
if request.method == 'POST':
form = Inputform(request.POST)
if form.is_valid():
cd = form.cleaned_data
print (cd['company'])
form.save()
return redirect('result')
urls
from inputform import views
from django.views.generic.list import ListView
from inputform.views import input
from django.views.generic import RedirectView
urlpatterns = patterns('',
url(r'^result_list/$',ResultView.as_view(),name='result'),
(r'^input/$', RedirectView.as_view(url='/result_list/')),
}
Result --views.py
class ResultView(ListView):
context_object_name = 'result_list'
template_name = 'result_list.html'
queryset = Result.objects.all()
def get_context_data(self, **kwargs):
context = super(ResultView, self).get_context_data(**kwargs)
context['input'] = Input.objects.all()
return context
Result -- html
{% for input_object in input %}
<table border="1" cellpadding="1">
<tr>
<td align="left">Company</td>
<td>{{input_object.company}}</td>
</tr>
<tr>
<td align="left">Region</td>
<td>{{input_object.region}}</td>
</tr>
...
</table>
{% endfor %}
Your code looks rather messy. Its not clear, how you initially pass the empty form to input template.
However, the problem may arise cause you do not pass the whole form to your template, but only {{ form.regionlist }}. So your inputs cannot write any data to db.
Render either the whole {{ form }} or each field one by one as you've done for regionlist field.
Moreover, you do not need to dublicate model field in forms.py if it does not actually override somehow the original field in models.py.
The root cause is actually in url.py:
Should delete "(r'^input/$', RedirectView.as_view(url='/result_list/')),",only to put redirect in views.py is enough.

How to display search results(django+haystack+elasticsearch) on the SAME page?

I included haystack+elasticsearch into my django project folowing by official haystack docs. Searching is working good, but results are displaying in separate page (search.html).I need show searching results on the same page I place searching query from.
I include search template into my base.html like this: {% include 'search/search.html' %}
My templates are in different dirs:templates/students/base.html and templates/search/search.html. As far I understand haystack uses its own /search/search.html for displaying search results. Which way can I change that behavior, how to display results on the same page? Please help!
urls.py:
from django.conf.urls import patterns, include, url
from django.contrib import admin
from students.views.students import StudentUpdateView
from students.views.students import StudentDeleteView
from students.views.students import StudentAddView
from students.views.groups import GroupDeleteView
from students.views.journal import JournalView
urlpatterns = patterns('',
#haystack search url
(r'^search/', include('haystack.urls')),
# main page url
url(r'^$', 'students.views.students.students_list', name ='home'),
search.html:
<form method="get" action="">
<table>
{{ form.as_table }}
<tr>
<td> </td>
<td>
<input type="text" name="q">
<button type="submit">Search</button>
</td>
</tr>
</table>
{% for student in page.object_list %}
<p><span style= "color:blue">Student:</
span> {{ student.object.first_name }} {{student.object.last_name }}</p>
<p>Ticket: {{ student.object.ticket }}</p>
<p>Group: {{ student.object.student_group }}</p>
{% empty %}
<p>No results found.</p>
{% endfor %}
seach_indexes.py:
from haystack import indexes
from students.models.students import Student
class StudentIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
last_name = indexes.CharField(model_attr='last_name')
def get_model(self):
return Student
def index_queryset(self, using=None):
return self.get_model().objects.all()
You can create a custom view and use that in your templates:
class CustomSearchView(SearchView):
template_name='/path/to/template.html'
and in your urls.py:
urlpatterns = patterns('',
url(r'^custom_search$', CustomSearchView.as_view(), name='custom_search'),
)
and in your templates, just call this view in your form:
<form method="get" action="{% url 'search:custom_search' %}">

Categories

Resources