I was making my own forms for CreateView and UpdateView with my html file because I don't want to display the form like this {{form.as_p}}.
forms.py
from django import forms
from .models import Post
class PostCreationForm(forms.ModelForm):
class Meta:
model = Post
fields = ('title', 'cover', 'text',)
widgets = {
'title': forms.TextInput(attrs={'class': 'title'}),
'cover': forms.FileInput(attrs={'class': 'image'}),
'text': forms.TextInput(attrs={'class': 'text'})
}
class PostDeleteForm(forms.ModelForm):
class Meta:
model = Post
fields = ('__all__')
views.py
from django.shortcuts import reverse
from django.http import HttpResponseRedirect
from django.views import generic
from .models import Post
from .forms import PostCreationForm, PostDeleteForm
class PostListView(generic.ListView):
model = Post
context_object_view = 'post_list'
template_name = 'forum/post_list.html'
class PostDetailView(generic.DetailView):
model = Post
context_object_view = 'post'
template_name = 'forum/post_detail.html'
class PostCreateView(generic.CreateView):
model = Post
form_class = PostCreationForm
template_name = 'forum/post_create.html'
def form_valid(self, form):
if form.is_valid():
response = form.save(commit = False)
response.author = self.request.user
response.save()
return HttpResponseRedirect(reverse('post_detail', args=[str(response.id)]))
class PostUpdateView(generic.UpdateView):
model = Post
context_object_view = 'post'
form_class = PostCreationForm
template_name = 'forum/post_edit.html'
def get_post(self, pk):
return get_object_or_404(Post, pk=pk)
def form_valid(self, form):
if form.is_valid():
response = form.save(commit = False)
response.save()
return HttpResponseRedirect(reverse('post_detail', args=[str(response.id)]))
class PostDeleteView(generic.DeleteView):
model = Post
context_object_view = 'post'
form_class = PostDeleteForm
template_name = 'forum/post_delete.html'
success_url = '/'
def get_post(self, pk):
return get_object_or_404(Post, pk=pk)
post_create.html
{% extends '_base.html' %}
{% block css %}
{% load static %}
<link rel="stylesheet" href="{% static 'css/post_create.css' %}">
{% endblock css %}
{% block title %}Questions{% endblock title %}
{% block content %}
<h1 class="m-title">New Post</h1>
<div class="form">
<form action="" method="post" enctype="multipart/form-data" id='postform'>
{% csrf_token %}
<p class="n-title"><label for="id_title">Title: </label></p>
<input id="id_title" type="text" name="title" class="title" maxlength="40" required>
<p class="n-image"><label for="id_cover">Image: </label></p>
<input id="id_cover" type="file" name="cover" class="image" required>
<p class="n-text"><label for="id_text">Text: </label></p>
<textarea id="id_text" placeholder="Enter your text here" name="text" class="text" form='postform' required></textarea>
<button class="btn btn-success" id="button" type="submit">Submit</button>
</form>
</div>
{% endblock content %}
And I also wanted to do for the UpdateView, but I don't know how to make the input fields display the current value of the post (title, text). The html file is the same as for CreateView. How do I make the input display the current value of the post that is being modified?
You probably don't want to be manually writing the html for the form like that.
If you must, you can pass in value like:
<input id="id_title" type="text" name="title" class="title" maxlength="40" value="{{ form.title.value }}" required>
Better would be to render the field using django so that your form field attributes like maxlength and required match your form class specification. For example, this will create the input for your title field:
{{ form.title }}
If you want more flexibility in styling look at crispy-forms or my preference floppyforms
Related
I'm new to Django and at this point , I set up a very simple post article page, I hope that when I successfully save the article, it will show the message of the bootstrap modal style.
model.py
from django.db import models
from django.utils import timezone
class Article(models.Model):
title = models.CharField(max_length=100,blank=False,null=False)
slug = models.SlugField()
content = models.TextField()
cuser = models.CharField(max_length=100)
cdate = models.DateField(auto_now_add=True)
mdate = models.DateField(auto_now=True)
forms.py
from .models import Article
from django.forms import ModelForm
class ArticleModelForm(ModelForm):
class Meta:
model = Article
fields = [
'title',
'content',
]
views.py
from django.shortcuts import render
from .forms import ArticleModelForm
from django.contrib.auth.decorators import login_required
from .models import Article
#login_required
def create_view(request):
form = ArticleModelForm(request.POST or None)
context={
'form':form
}
if form.is_valid():
article_obj = Article(cuser=request.user)
form = ArticleModelForm(request.POST,instance=article_obj)
form.save()
context['saved']=True
context['form']=ArticleModelForm()
return render(request,'article/create.html',context= context)
my template > article/create.html
{% extends 'base.html' %}
{% block content %}
<form method="POST">
{% csrf_token %}
{{form.as_p}}
<div><button data-bs-toggle="modal" data-bs-target="#saveModal" type="submit">create</button></div>
</form>
{% if saved %}
<div class="modal fade" id="saveModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h3>save successfully!</h3>
<button type="button" class="btn-close" data-bs-dismiss="modal">
</button>
</div>
</div>
</div>
</div>
{% endif %}
{% endblock content %}
I use the saved variable in views.py to determine whether the content of the article has been successfully saved, and if so, set it in the context
In the template if saved exists, the modal related code will be presented, but this way
unsuccessful.
The problem is with modal itself, whenever you are submitting the form the form gets submitted but when it comes with saved=True then also there is a need for clicking the button of modal to display the message saved successfully which is not possible. So the other alternative is to use alert classes of boostrap (you are free to apply your own styling to it but below is just an example), so try this template:
<form method="POST">
{% csrf_token %}
{{form.as_p}}
<div><button type="submit">create</button></div>
</form>
{% if saved %}
<div class="alert alert-success alert-dismissible fade show" role="alert">
<strong>Saved successfully.</strong>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
{% endif %}
You should maintain separate conditions for GET and POST request and also do re-render the template with saved=True so try this view:
#login_required
def create_view(request):
if request.method == "POST":
article_obj = Article(cuser=request.user)
form = ArticleModelForm(request.POST, instance=article_obj)
if form.is_valid():
form.save()
context = {
"form": ArticleModelForm(),
"saved": "yes"
}
return render(request, "article/create.html", context)
else:
print("form is not valid")
else: # GET method
form = ArticleModelForm()
return render(request, 'article/create.html', {"form": form})
I am following a Django course on youtube, and I need a small change in my Django form.
The form looks like:
Rendering the form fields in 'edit-user.html' template:
<form action="" method="POST" enctype="multipart/form-data">
{% csrf_token %}
{% for field in form %}
<div class="form-input">
<label for="id_{{field.label}}">{{field.label}}</label>
{{field}}
</div>
{% endfor %}
<div id="lrf-options">
<div id="lrf-btn">
<input class="btn" type="submit" value="Update">
</div>
</div>
</form>
What I am actually asking:
So in Avatar Currently: 1044-3840x2160.jpg, I want actual image to display here instead of <filename>.
I think maybe I need to change my forms.py to generate <img> tag instead if <a> tag but I don't know how to do this.
models.py
class UserModel(AbstractUser):
name = models.CharField(max_length = 90)
email = models.EmailField(unique = True)
about = models.TextField(blank = True, null = True)
avatar = models.ImageField(null=True, default="avatar.svg")
USERNAME_FIELD = 'email'
and forms.py is:
class UserForm(forms.ModelForm):
class Meta:
model = UserModel
fields = ['avatar', 'name', 'username', 'email', 'about']
and in views.py the update function is:
def editUserProfile(request):
user = request.user
form = UserForm(instance=user)
if request.method == 'POST':
form = UserForm(request.POST, request.FILES, instance = user)
if form.is_valid():
form.save()
return redirect('chat:userProfileView', uname = user.username)
context = {
'form' : form
}
return render(request, 'chat/edit-user.html', context)
You can add request.user.avatar.url instead of currently url.Hide input and use jquery to give a click to input:
<div class="form-input">
<label for="id_Avatar">Avatar</label>
Currently:
<img id="current-avatar" src="{{request.user.avatar.url}}"/>
<br>
<a id="change-avatar">Change</a>
<input id="avatar-input" style="display:none;" type="file" name="avatar" accept="image/*" id="id_avatar">
</div>
<script>
$("#change-avatar").click(function(){
$("#avatar-input").click();
});
</script>
If you don't have jQuery add this to your main HTML:
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
</head>
I make a site with multiple users, making posts with images and ability to add/remove friends.
So it's easy to make two different pages for post list and creating a new one. But of course it looks better when you can read posts and make new at the same place.
As I understand (learn django for less than a month), I can't connect 2 views to the same url, so the most logical way I see is to join 2 views in one, I also tried to play with template inheriting to render post form by including template, but actually it doesn't work.
Below you can see my views, Post model, and templates. Thank you for attention.
views.py:
from braces.views import SelectRelatedMixin
from . import models
from django.views import generic
from django.contrib.auth.mixins import LoginRequiredMixin
class PostList(SelectRelatedMixin, generic.ListView):
model = models.Post
select_related = ('user',)
class CreatePost(LoginRequiredMixin, SelectRelatedMixin, generic.CreateView):
fields = ('post_message', 'post_image')
model = models.Post
select_related = ('user',)
def form_valid(self, form):
self.object = form.save(commit = False)
self.object.user = self.request.user
self.object.save()
return super().form_valid(form)
models.py:
import misaka
class Post(models.Model):
user = models.ForeignKey(User, on_delete = models.CASCADE, related_name = 'posts')
posted_at = models.DateTimeField(auto_now = True)
post_message = models.TextField()
message_html = models.TextField(editable = False)
post_image = models.ImageField(upload_to = 'postpics', blank = True)
def __str__(self):
return self.post_message
def save(self, *args, **kwargs):
self.message_html = misaka.html(self.post_message)
super().save(*args, **kwargs)
def get_absolute_url(self):
return reverse('posts:all')
class Meta:
ordering = ['-posted_at']
unique_together = ['user', 'post_message']
urls.py:
app_name = 'posts'
urlpatterns = [
path('', views.PostList.as_view(), name = 'all'),
path('new/', views.CreatePost.as_view(), name = 'create'),
]
post_form.html (template, that allows to make a new post, which will be seen in post_list.html):
{% extends 'posts/post_base.html'%}
{% block post_content %}
<div class="post-form">
<form action="{% url 'posts:create' %}" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<p>{{ form.post_message }}</p>
<p>{{ form.post_image }}</p>
<input id='post-submit' type="submit" value="Post">
</form>
</div>
{% endblock %}
post_list.html:
{% extends 'posts/post_base.html'%}
{% block post_content %}
<div class="post-container">
{% for post in post_list %}
<div class="current-post-container">
{% include 'posts/_post.html'%}
</div>
{% endfor %}
</div>
{% endblock %}
_post.html(pages, which render by Misaka):
<div class="post-info">
<h5 id='post-owner' >{{post.user.first_name}} {{post.user.last_name}}</h5>
<h6>{{ post.posted_at }}</h6>
<p>{{ post.message_html|safe }}</p>
<div>
<img class='post-image' src="/media/{{ post.post_image }}" alt="">
<div>
{% if user.is_authenticated and post.user == user and not hide_delete %}
<a href="{% url 'posts:delete' pk=post.pk %}" title = 'delete'>Delete</a>
{% endif %}
</div>
</div>
</div>
post_base.html:
{% extends 'base.html' %}
{% block content%}
{% block prepost %}{% endblock %}
{% block post_content %}{% endblock %}
{% block post_post %}{% endblock %}
{% endblock %}
EDIT:
Task was solved. I added two template_name strings to both of my views, so now they look like:
CreatePost in views.py:
class CreatePost(LoginRequiredMixin, SelectRelatedMixin, generic.CreateView):
fields = ('post_message', 'post_image')
model = models.Post
select_related = ('user',)
template_name = 'posts/post_list.html'
template_name = 'posts/post_form.html'
def form_valid(self, form):
self.object = form.save(commit = False)
self.object.user = self.request.user
self.object.save()
return super().form_valid(form)
PostList in views.py:
class PostList(SelectRelatedMixin, generic.ListView):
model = models.Post
select_related = ('user',)
template_name = 'posts/post_list.html'
template_name = 'posts/post_form.html'
You can put the post_create_form on the same page as post_list_view there is no need to make a separate view for post creation but You need to make ones for editing and deleting.
You can give all of these views the same HTML page with different URLs.
Using template_name = 'example/example.html' ,in Class_Based_Views.
I hope I understand your problem if not clarify more why you can't join two views in one.
def posts(request):
posts = Post.objects.all()
form = PostForm(request.POST or None, request.FILES or None)
if request.method == "POST":
if form.is_valid():
...
context={
'posts' : page_obj,
'create_or_update_post_form' : form,
}
return render(request, 'post/posts.html',context)
Do you struggle to do this in Class-based-view?
You can do easily with django class based views.
Create views as
from django.views.generic import ListView
from django.views.generic.edit import CreateView
class ModelCreate(CreateView):
model = ModelName
fields = ['field1', 'field2']
template_name = 'same_page.html'
success_url = reverse_lazy('list_view')
class ModelList(CreateView, ListView):
model = ModelName
fields = ['field1', 'field2']
paginate_by = 5
template_name = 'same_page.html'
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['form'] = self.get_form()
return context
# If form post redirect to ModelCreate View
def post(self, request, *args, **kwargs):
return ModelCreate.as_view()(request)
app/urls.py
from django.urls import path
from app import views
path('list', views.ModelList.as_view(), name='list_view'),
path('add', views.ModelCreate.as_view(), name='add_view'),
Finally in templates/same_page.html
<div class="row">
<div class="col-sm-5">
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{form.as_p}}
<button type="submit" value="submit" class="btn btn-primary btn-sm float-right">Submit</button>
</form>
</div>
<div class="col-sm-5">
{% if object_list %}
{% for object in object_list %}
<p>{{object.field1}}</p>
<p>{{object.field2}}</p>
{% endfor %}
{% endif %}
</div>
</div>
Hope, this helps.
I am creating a Django CRM where I am using the forms to acquire data about any company. What I want to do is when I or anybody has written about the details of any companies and submitted them - they are going to be saved as card views in companies.html.
Can anybody help me regarding this?
companies.html:
{% extends 'base.html' %}
{% load static %}
{% block content %}
<div class="container-fluid">
<!-- Page Heading -->
<div class="d-sm-flex align-items-center justify-content-between mb-4 mt-4">
<i class="fas fa-plus fa-sm text-white-50"></i> Create Company
</div>
<div class="text-center">
<a class="small" href="{% url 'dashboard' %}">Back</a>
</div>
</div>
{% endblock content %}
company-create.html:
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<!-- Begin Page Content -->
<div class="container-fluid">
<!-- Page Heading -->
<div class="d-sm-flex align-items-center justify-content-between mb-4 mt-4">
<h1 class="h3 mb-0 text-gray-800">Company Create</h1>
</div>
<!-- Main Content Here -->
<div class="card o-hidden border-0 shadow-lg my-5">
<div class="card-body p-0">
<div class="row">
<div class="col-lg-3"></div>
<div class="col-lg-6">
<div class="p-5">
<div class="text-center">
<h1 class="h4 text-gray-900 mb-4">Create a Company!</h1>
</div>
<form method="POST" action="" enctype="multipart/form-data">
{% csrf_token %}
{{user_form | crispy}}
{{profile_form | crispy}}
<button type="submit" class="btn btn-primary btn-block">Update</button>
</form>
<hr>
<div class="text-center">
<a class="small" href="{% url 'dashboard' %}">Back</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- /.container-fluid -->
{% endblock content %}
forms.py:
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from apps.userprofile.models import Profile
class SignUpForm(UserCreationForm):
first_name = forms.CharField(max_length=30, required=False, help_text='Optional')
last_name = forms.CharField(max_length=30, required=False, help_text='Optional')
email = forms.EmailField(max_length=254, help_text='Enter a valid email address')
class Meta:
model = User
fields = [
'username',
'first_name',
'last_name',
'email',
'password1',
'password2',
]
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = [
'username',
'first_name',
'last_name',
'email',
]
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = [
'bio',
'phone_number',
'birth_date',
'profile_image'
]
class CompanyForm(forms.Form):
name = forms.CharField(label="Enter Company Name",max_length= 50)
email = forms.EmailField(label="Enter Email")
phone_number = forms.CharField(label="Enter Phone Number",max_length=12)
contact_name = forms.CharField(label="Enter Contact Persons Name",max_length=50)
file = forms.FileField()
class Meta:
model = User
fields = [
'name',
'email',
'phone_number',
'username'
'profile_image',
]
views.py:
from django.shortcuts import render
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse_lazy
from django.views.generic import TemplateView, CreateView
from .forms import SignUpForm, UserForm, ProfileForm, CompanyForm
from django.contrib.auth.models import User
from django.http import HttpResponseRedirect
from django.contrib import messages
from apps.userprofile.models import Profile
class HomeView(TemplateView):
template_name = 'common/home.html'
class DashboardView(LoginRequiredMixin, TemplateView):
template_name = 'common/dashboard.html'
login_url = reverse_lazy('home')
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super().get_context_data(**kwargs)
# Add in a QuerySet of all the books
print(self.request.user.id)
context['book_list'] = self.request.user
return context
class SignUpView(CreateView):
form_class = SignUpForm
success_url = reverse_lazy('home')
template_name = 'common/register.html'
class MessageView(TemplateView):
template_name = 'common/messages.html'
class CompanyView(TemplateView):
template_name = 'common/companies/companies.html'
class CompanyCreateView(LoginRequiredMixin, TemplateView):
user_form = CompanyForm
template_name = 'common/companies/company-create.html'
def post(self, request):
post_data = request.POST or None
file_data = request.FILES or None
user_form = CompanyForm(post_data)
if user_form.is_valid():
user_form.save()
messages.success(request, 'Your company was successfully created!')
return HttpResponseRedirect(reverse_lazy('comapanies'))
context = self.get_context_data(
user_form=user_form
)
return self.render_to_response(context)
def get(self, request, *args, **kwargs):
return self.post(request, *args, **kwargs)
from django.http import HttpResponseRedirect
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse_lazy
from django.contrib import messages
from .forms import UserForm, ProfileForm
from django.contrib.auth.models import User
from apps.userprofile.models import Profile
class ProfileView(LoginRequiredMixin, TemplateView):
template_name = 'common/profile.html'
class ProfileUpdateView(LoginRequiredMixin, TemplateView):
user_form = UserForm
profile_form = ProfileForm
template_name = 'common/profile-update.html'
def post(self, request):
post_data = request.POST or None
file_data = request.FILES or None
user_form = UserForm(post_data, instance=request.user)
profile_form = ProfileForm(post_data, file_data, instance=request.user.profile)
if user_form.is_valid() and profile_form.is_valid():
user_form.save()
profile_form.save()
messages.success(request, 'Your profile was successfully updated!')
return HttpResponseRedirect(reverse_lazy('profile'))
context = self.get_context_data(
user_form=user_form,
profile_form=profile_form
)
return self.render_to_response(context)
def get(self, request, *args, **kwargs):
return self.post(request, *args, **kwargs)
Along with the codes I am also attaching some screenshots of the CRM:
Dashboard:
Like the cards called companies and contacts I want the company details to be shown in cards -
Companies:
Here after clicking on the create contact button we are redirected to the company create form.
Create-Company:
Now here after the update button is clicked with the filled out form details I want to redirect the form to the contacts page with the newly added company details shown below the create company button in a card form.
This is the approach as you requested an example using plain function as view not class. You will need to do some settings in settings.py such as install Pillow (if you are not done it already). See if it can help you.
models.py
from django.db import models
class CompanyDetail(models.Model):
name = models.CharField(label="Enter Company Name",max_length= 50)
email = models.EmailField(label="Enter Email")
phone_number = models.CharField(label="Enter Phone Number",max_length=12)
contact_name = models.CharField(label="Enter Contact Persons Name",max_length=50)
image = models.ImageField(upload_to='tour_images', blank=True)
views.py
from django.shortcuts import render,
from .models import CompanyDetail
def show_company_cards(request):
company_details = CompanyDetail.objects.all()
return render(request, 'company.html', {'company_details': company_details})
company.html
{% extends 'base.html' %}
{% load static %}
{% block content %}
<div class="container-fluid">
<!-- Page Heading -->
<div class="d-sm-flex align-items-center justify-content-between mb-4 mt-4">
<i class="fas fa-plus fa-sm text-white-50"></i> Create Company
</div>
<div class="text-center">
<a class="small" href="{% url 'dashboard' %}">Back</a>
</div>
<!-- this is the place of the closing container fluid that is in the bottom -->
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" rel="stylesheet">
<div class="container">
<div class="row">
{% for detail in company_details %}
<div class="col-md-3">
<div class="contact-box center-version">
<img alt="image" class="img-circle" src="{{ detail.image.url}}">
<h3 class="m-b-xs"><strong>{{detail.name}}</strong></h3>
<div class="font-bold">{{detail.contact_name}}</div>
<address class="m-t-md">
<strong>{{detail.email}}</strong><br>
<abbr title="Phone">P:</abbr> {{detail.phone_number}}
</address>
</div>
</div>
{% endfor %}
</div>
</div>
<!-- container fluid closer (test to see if it works) if no remove an put it back-->
</div>
{% endblock content %}
model.py
class Venue(models.Model):
venue_Name = models.CharField(max_length=100)
place = models.CharField(max_length=50)
rent = models.IntegerField()
parking_area = models.IntegerField()
class Decoration(models.Model):
rate = models.IntegerField()
I have printed the values in database as radio buttons what i want to do is that i want to get the total sum i.e venue.rent + decoration.rate and print it in another page What shoud i give in form action I'm not that familiar with forms.
html
<form action="{% %}" method="post">
{% for venue in venues %}
<input type="radio" name=venue value=venue.rent />{{ venue.venue_Name}}
{% endfor %}
{% for decoration in decorations %}
<input type="radio" name=decor value=decoration.rate />{{ decoration.rating }}
{% endfor %}
<input type="submit" value=" " />
</form>
what should i write in view and urls to get the sum
You can use Django's form for validation and parsing. For that you would set up a form like so:
forms.py
from django import forms
class TotalSumForm(forms.Form):
venue = forms.ModelChoiceField(queryset=Venue.objects.all(), required=True)
decoration = forms.ModelChoiceField(
queryset=Decoration.objects.all(), required=True)
def get_total(self):
# send email using the self.cleaned_data dictionary
return self.cleaned_data['venue'].rent +\
self.cleaned_data['decoration'].rate
And then using a class based view, add the result to context upon submission.
views.py
from myapp.forms import TotalSumForm(
from django.views.generic.edit import FormView
class TotalCost(FormView):
template_name = 'your_template.html'
form_class = TotalSumForm
success_url = '/thanks/'
def form_valid(self, form):
# This method is called when valid form data has been POSTed.
total_result = form.get_total()
# return back to your_template.html with the total in context
return self.render_to_response(self.get_context_data(
form=form, total=total_result))
The urls are pretty simple:
urls.py
from django.conf.urls import patterns, url
import myapp.views
urlpatterns = patterns(
'',
url(r'^total_calc/$', myapp.views.TotalCost.as_view(), name='calculate_total'),
)
Your html could be modified like so
your_template.html
<html>
<body>
<h1>TEST SUCCESFULL!</h1>
{% if total %}
<p>Total cost for venue and decoration: {{ total }}</p>
{% endif %}
<form action="{% url 'calculate_total' %}" method="post">
{{ form.as_p }}
<input type="submit" value="Calculate Total" />
</form>
</body>
</html>