I'm trying to implement profile picture field for users. The following is the code for each file for the implementation I tried, forms.py, models.py, views.py, and urls.py.
I use a IDE (vscode) to debug django, and I placed a breakpoint on the user.avatar = form.cleaned_data['avatar'] line in views.py below, to quickly check if cleaned_data['avatar'] is filled as user input, as I expect.
However, even after I upload a file on the url, submit, the line shows None while expected a image object, and of course it doesn't save anything so no change to the database either.
#
# forms.py
# accounts/forms.py
#
from accounts.models import UserProfile
# ..
class UserProfileForm(forms.ModelForm):
avatar = forms.ImageField(label=_('Avatar'))
class Meta:
model = UserProfile
fields = [
'avatar',
]
def __init__(self, *args, **kwargs):
super(UserProfileForm, self).__init__(*args, **kwargs)
self.fields['avatar'].required = False
#
# models.py
# accounts/models.py
#
from django.contrib.auth.models import User
from PIL import Image
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
avatar = models.ImageField(upload_to="images", blank=True, null=True)
# note: I also did "python manage.py makemigrations accounts; python manage.py migrate accounts;"
#
# views.py
# accounts/views.py
#
class UserProfileView(FormView):
template_name = 'accounts/profile/change_picture.html'
form_class = UserProfileForm
def form_valid(self, form):
user = self.request.user
user.avatar = form.cleaned_data['avatar']
user.save()
messages.success(self.request, _('Profile picture has been successfully updated'))
return redirect('accounts:change_profile_picture')
#
# urls.py
# accounts/urls.py
#
from .views import UserProfileView
urlpatterns = [
# ..
path('change/profile_picture/', UserProfileView.as_view(), name='change_profile_picture'),
]
What is wrong with the code? Thanks.
edit
as requested, the html accounts/profile/change_picture.html
{% extends 'layouts/html.html' %}
{% load static %}
{% load bootstrap4 %}
{% load i18n %}
{% block content %}
{% include 'head.html' %}
<body>
{% include 'navbar.html' %}
<div id="content" name="content" class="main container">
<div class="w-100 p-3"></div>
<h2>{% trans 'Change profile picture' %}</h2>
<form method="post">
{% csrf_token %}
{% bootstrap_form form %}
<button class="btn btn-success">{% trans 'Change' %}</button>
</form>
<div class="w-100 p-3"></div>
</div>
{% include 'footer.html' %}
</body>
{% endblock %}
Add enctype="multipart/form-data" to the form:
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{% bootstrap_form form %}
<button class="btn btn-success">{% trans 'Change' %}</button>
</form>
Related
I have read many questions, followed the Django docs, googled for answers, and I can't get to upload a file in my Django app.
There is no error, form.cleaned_data shows the file and the other foreign key field, but no upload to media folder and no record in my db.
I can't figure what am I missing. Any help would be much appreciated.
#models.py
class ReportFile(models.Model):
report = models.ForeignKey(Report, on_delete=models.CASCADE)
file = models.FileField(upload_to='files/reports')
uploaded = models.DateTimeField(auto_now_add=True)
uploaded_by = models.ForeignKey(User, on_delete=models.CASCADE)
def save(self, *args, **kwargs):
user = get_current_user()
if user and not user.pk:
user = None
if not self.pk:
self.creado_por = user
###UPDATE: As Iain Shelvington kindly pointed out, I was missing the call to super.
super(ReportFile, self).save(*args, **kwargs)
#forms.py
class FileForm(forms.ModelForm):
class Meta:
model = ReportFile
fields = ['file','report']
This is the view I'm using, based on what I've read
#views.py
def CreateFile(request):
if request.method == 'POST':
form = FileForm(request.POST,request.FILES)
if form.is_valid():
form.save()
print(form.cleaned_data)
#OUTPUT: HTTP POST /file 200 [0.09, 127.0.0.1:59053]
# {'file': <InMemoryUploadedFile: test-image.png (image/png)>, 'report': <Report: 49>}
return render(request, 'segcom/file_upload.html', {'form': form,})
else:
form = FileForm()
context = {
'form':form,
}
return render(request, 'segcom/file_upload.html', context)
The relevant settings that I know of
#settings.py
# Media Root
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
MEDIA_URL = '/media/'
This is the template I'm using
{% extends "layouts/base.html" %}
{% load crispy_forms_tags %}
{% block title %} File Upload Test {% endblock %}
<!-- Specific CSS goes HERE -->
{% block stylesheets %}{% endblock stylesheets %}
{% block content %}
<div class="row">
{{ form.non_field_errors }}
<form method = 'post' action="{% url 'file_upload' %}" id="report-form" enctype="multipart/form-data">
{% csrf_token %}
{{form|crispy}}
<div class="modal-footer">
<button type="submit" form="report-form" class="btn btn-primary">Guardar</button>
</div>
</form>
</div>
{% endblock content %}
<!-- Specific Page JS goes HERE -->
{% block javascripts %}
{% endblock javascripts %}
So I'm trying to make a form to appear after the user is authenticated. When the user authenticated then the form appears on the home page but it never appears. I'm pretty sure I'm doing something wrong either in paths or view but can't figure this out. When I do the same code to external new template it works fine but I want the form to appear on the same page. Here is the code:
views.py
def vpn(request):
form = vpn_form(request.POST or None)
if form.is_valid():
form.save()
context ={
'form': form
}
return render(request, 'loginas/home.html', context)
urls.py
urlpatterns = [
# /
path('', views.home, name='home'),
# TEMPORARY
path('signin', views.sign_in, name='signin'),
path('signout', views.sign_out, name='signout'),
path('callback', views.callback, name='callback'),
path('', views.vpn, name='vpn'),
models.py
class VPN(models.Model):
name = models.CharField(max_length=125)
surname = models.CharField(max_length=125)
description = models.TextField()
date_to = models.DateField()
date_from = models.DateField()
forms.py
from .models import VPN
class vpn_form(forms.ModelForm):
class Meta:
model = VPN
fields = ('name', 'surname', 'description', 'date_to', 'date_from')
home template
{% extends "loginas/layout.html" %}
{% load static %}
{% block content %}
<div class="container">
<h1 class="d-flex justify-content-center"> </h1>
<p class="d-flex justify-content-center"></p>
{% if user.is_authenticated %}
<form>
{{form.as_p}}
</form>
{% else %}
<div class="d-flex justify-content-center">
Login
</div>
{% endif %}
{% endblock %}
And the page just loads empty no form present no errors in the console. Any help how to do this would be great
I am creating a small system that has two users, both of these users need singup forms.
To allow social accounts and ease of use i have used django_allauth. But i ran into a problem of creating two custom signin forms with different fields.
i have used multiple stackoverflow answers but unfortunately none have helped if anything they are now adding to the confusion ...
Multiple user type sign up with django-allauth
Multiple signup, registration forms using django-allauth
I find it hard to believe that this is not a use case that comes up a lot, someone must have done this before. My current code has 2 custom signup forms, 2 custom sign-up views and two custom URLs where the forms should be rendered. But they are both using the same form and I have no idea why.
can anyone shed any light on the situation?
from .models import GraduateUserProfile
from django import forms
from allauth.account.forms import SignupForm
import datetime
def year_choices():
return [(r, r) for r in range(2015, datetime.date.today().year + 1)]
def current_year():
return datetime.date.today().year
class GraduateUserSignupForm(SignupForm):
def __init__(self, *args, **kwargs):
super(GraduateUserSignupForm, self).__init__(*args, **kwargs)
self.fields['first_name'] = forms.CharField(required=True)
self.fields['last_name'] = forms.CharField(required=True)
self.fields['phone_number'] = forms.CharField(required=True)
self.fields['degree_course_name'] = forms.CharField(required=True)
self.fields['graduation_year'] = forms.TypedChoiceField(coerce=int, choices=year_choices, initial=current_year)
def save(self, request):
user = super(GraduateUserSignupForm, self).save(request)
user.first_name = self.cleaned_data.get('first_name')
user.last_name = self.cleaned_data.get('last_name')
user.is_graduate = True
user.save()
graduate = GraduateUserProfile.objects.create(user=user)
graduate.phone_number = self.cleaned_data.get('phone_number')
graduate.graduation_year = self.cleaned_data.get('phone_number')
graduate.degree_course = self.cleaned_data.get('degree_course')
graduate.save()
return user
class CompanyUserSignupForm(SignupForm):
def __init__(self, *args, **kwargs):
super(CompanyUserSignupForm, self).__init__(*args, **kwargs)
self.fields['degree_course_name'] = forms.CharField(required=True)
self.fields['degree_course_test'] = forms.CharField(required=True)
def save(self, request):
user = super(CompanyUserSignupForm, self).save(request)
return user
from .forms import CompanyUserSignupForm, GraduateUserSignupForm
from allauth.account.views import SignupView
class CompanyUserSignupView(SignupView):
template_name = 'account/company_signup.html'
form_class = CompanyUserSignupForm
redirect_field_name = 'next'
view_name = 'company_signup'
success_url = None
def get_context_name(self, **kwargs):
ret = super(CompanyUserSignupView, self).get_context_data(**kwargs)
ret.update(self.kwargs)
return ret
company_signup = CompanyUserSignupView.as_view()
class GraduateUserSignupView(SignupView):
template_name = 'account/graduate_signup.html'
form_class = GraduateUserSignupForm
redirect_field_name = 'next'
view_name = 'graduate_signup'
success_url = None
def get_context_name(self, **kwargs):
ret = super(GraduateUserSignupView, self).get_context_data(**kwargs)
ret.update(self.kwargs)
return ret
grad_signup = GraduateUserSignupView.as_view()
urlpatterns = [
path('business/signup', view=company_signup, name='company_signup'),
path('graduate/signup', view=grad_signup, name='graduate_signup'),
]
{% load i18n %}
{% block head_title %}{% trans "Signup" %}{% endblock %}
{% block content %}
<h1>{% trans "Sign Up" %}Grad Sign up</h1>
<p>{% blocktrans %}Already have an account? Then please sign in.{% endblocktrans %}</p>
<form class="signup" id="company_signup_form" method="post" action="{% url 'graduate_signup' %}">
{% csrf_token %}
{{ form.as_p }}
{% if redirect_field_value %}
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
{% endif %}
<button type="submit">{% trans "Sign Up" %} »</button>
</form>
{% endblock %}
{% extends "account/base.html" %}
{% load i18n %}
{% block head_title %}{% trans "Signup" %}{% endblock %}
{% block content %}
<h1>{% trans "Sign Up" %}BUSINESS LOGIN</h1>
<p>{% blocktrans %}Already have an account? Then please sign in.{% endblocktrans %}</p>
<form class="signup" id="graduate_signup_form" method="post" action="{% url 'company_signup' %}">
{% csrf_token %}
{{ form.as_p }}
{% if redirect_field_value %}
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
{% endif %}
<button type="submit">{% trans "Sign Up" %} »</button>
</form>
{% endblock %}
I have just figure it out. If you remove 'signup': 'accounts.forms.GraduateUserSignupForm', my forms are appearing correctly
EDIT: After a few days i found that the original allauth sign up view is still available to view. So i used this little peice of code
path('accounts/signup/', page_not_found, {'exception': Exception('Not Found')}, name="default_signup"),
to throw a 404 if anyone tried to view it
I have these two functions, one of them (first one) adds a new entry and the second one edits the entry:
def add_entry(request):
if request.method == 'POST':
form = AddForm(request.POST)
if form.is_valid():
title = form.cleaned_data["title"]
content = form.cleaned_data["content"]
if util.get_entry(title) is None:
util.save_entry(title, content)
return redirect('entry', title)
else:
return render(request, "encyclopedia/add_entry.html", {
"form": AddForm(),
"title": title
})
return render(request, "encyclopedia/add_entry.html", {
"form": AddForm()
})
def edit_entry(request, title):
content = util.get_entry(title)
if request.method == 'POST':
form = AddForm(request.POST)
if form.is_valid():
title = form.cleaned_data["title"]
content = form.cleaned_data["content"]
util.save_entry(title, content)
return redirect('entry', title)
return render(request, "encyclopedia/edit_entry.html", {
"title": title,
"content": content
Here is my edit_entry.html page:
{% extends "encyclopedia/layout.html" %}
{% block title %}
Edit page
{% endblock %}
{% block body %}
<form action="{% url 'edit_entry' title %}" method="POST">
{% csrf_token %}
<h5>Title</h5>
<input type="text" value="{{ title }}">
<h5>Content</h5>
<textarea cols="30" rows="10">{{ content }}</textarea>
<input type="submit" value="Save Editing">
</form>
{% endblock %}
This is add_entry.html template
{% extends "encyclopedia/layout.html" %}
{% block title %}
Add new entry
{% endblock %}
{% block body %}
<h1>Create a new page</h1>
{% if title %}
<h6 style="color: red;">"{{title}}" page is already exists. Please, enter a different title</h6>
{% endif %}
<form action="{% url 'add_entry' %}" method="POST">
{% csrf_token %}
{{ form }}
<input type="submit" value="Create">
</form>
{% endblock %}
And here is my urls.py:
from django.urls import path
from . import views
urlpatterns = [
path("", views.index, name="index"),
path("wiki/<str:title>", views.entry, name="entry"),
path("search", views.search, name="search"),
path("add_entry", views.add_entry, name="add_entry"),
path("wiki/<str:title>/edit_entry", views.edit_entry, name="edit_entry")
]
My entry view:
def entry(request, title):
if title not in util.list_entries():
return render(request, "encyclopedia/error.html", {
"error": "Page Not Found",
"query": title
})
else:
return render(request, "encyclopedia/entry.html", {
"entry": markdown2.markdown(util.get_entry(title)),
"title": title
})
The issue here when I click to save the content of the page doesn't change, I want to save the edits and display it with new content. Instead, it returns an old form with the old content (like doesn't change).
EDIT: based on your comments, I think it is better to start over.
Since you are doing some simple create and update, it maybe better to use generic views. Here is an example.
1.First and formost, you need a model.
in models.py,
from django.db import models
class Entry(models.Model):
title = models.CharField(max_length=200)
content = models.TextField(max_length=2000)
2. in your forms.py
Note: this is not necessary if you want to just use django default form. Because class-based generic views will automatically generate forms for you. However, if you need to add widget, or to add attributes (for example, add css class or id), you need to generate a customform.
from django import forms
from .models import Entry
class EntryForm(forms.ModelForm):
class Meta:
model = Entry
fields = ('title', 'content')
widgets = {
'title': forms.TextInput(attrs={'placeholder': 'Title'}),
'content': forms.TextInput(attrs={'class': 'content'}),
}
3. views.py
from .models import Entry
from django.views.generic.edit import CreateView, UpdateView
class CreateEntry(CreateView):
model=Entry
template_name = 'create_edit_entry.html' # this is the template, you might need to change its path.
form_class= EntryForm # this is added because we are using customform
success_url = '/' #this can be changed
class UpdateEntry(UpdateView):
model=Entry
template_name = 'create_edit_entry.html'
form_class= EntryForm
4. urls.py
from django.urls import path
from .views import CreateEntry, UpdateEntry
urlpatterns = [
path('entry/', CreateEntry.as_view(), name='create_entry'),
path('entry/<int:pk>', UpdateEntry.as_view(), name='update_entry'),
]
5. admins.py
from django.contrib import admin
from .models import Entry
class EntryAdmin(admin.ModelAdmin):
list_display = (('id', 'title', 'content'))
admin.site.register(Entry, EntryAdmin)
6. templates (create_edit_entry.html)
{% extends 'base.html' %}
{% block extrahead %}
{% load static %}
{% endblock %}
{% block content %}
<form action="." method="POST">
{% csrf_token %}
{{ form }}
<button type="submit">SUBMIT</button>
</form>
{% endblock %}
After you update all these files and update mysite/urls.py, you will 1) open http://127.0.0.1:8000/entry to add an entry. Check if the entry is created in your admin page. 2) then you will open http://127.0.0.1:8000/entry/1 (if the id=1) to see if your original entry is shown. 3) then you will update the form, and check if the update is successful or not in your admin.
This backbone should be able to get you started. Note that I did not put DetailView, ListView, so you need to check if the object is created and updated in your admin page. Of cause, you can add DetailView and ListView by yourself (check out django document here to learn more about generic views).
**************************************earlier answer **************
1. First thing first, it is always helpful to access form.errors when you are having trouble with forms. What you do is to add else: print(form.errors) like the following:
if form.is_valid():
# other code
else:
print(form.errors)
2.
Your edit_entry.html change to something like below: I guess you wanted use your own styling (add Title, Content etc) to the form, so you did not use {{form}}. If what I suggest worked, you can add form styling later.
{% extends "encyclopedia/layout.html" %}
{% block title %}
Edit page
{% endblock %}
{% block body %}
<form action="{% url 'edit_entry' title %}" method="POST">
{% csrf_token %}
{{form}}
</form>
{% endblock %}
3. your edit_entry view:
def edit_entry(request, title):
entry = get_object_or_404(Entry, title=title) # i assume your Model name is "Entry"
if request.method == 'POST':
form = AddForm(request.POST, instance = entry)
if form.is_valid():
print('under form.is_valid) # add this line to keep track
title = form.cleaned_data["title"]
content = form.cleaned_data["content"]
form.save()
return redirect('entry', title=entry.title)
else:
print(form.errors)
else:
form = AddForm(instance = entry)
return render(request, "encyclopedia/edit_entry.html", {
'form': form})
I cannot save the data taken from the form to database. The form is displayed properly and it seems that I can submit. Whenever I was redirected to "project_list.html", I cannot see the new project.
I also checked the admin site to whether new instance is saved to model but it seems that something is wrong with my code.
Here is my files:
model.py
class Project(models.Model):
project_id = models.CharField(max_length=30)
project_name = models.CharField(max_length=100)
view.py
def projects_list(request):
projects = Project.objects.all()
table = ProjectTable(Project.objects.all())
RequestConfig(request, paginate={'per_page':25}).configure(table)
return render(request, 'portal/project/list.html', {'projects':
projects, 'table': table})
def project_add(request):
if request.method == 'POST':
form = ProjectAddForm(request.POST)
if form.is_valid():
form.save()
return redirect('project_list',)
else:
form = ProjectAddForm()
return render(request, 'portal/project/add.html', {'form': form})
forms.py
from django import forms
from .models import Project
class ProjectAddForm(forms.ModelForm):
class Meta:
model = Project
fields = ['project_id', 'project_name',]
add.html
{% extends 'portal/base.html' %}
{% block title %}Add Project{% endblock title %}
{% block content %}
<div class="col-sm-10 offset-sm-1 text-center">
<form action="{% url 'portal:projects_list' %}" method="POST">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit">
</form>
</div>
{% endblock content %}
projects_list.html
{% extends 'portal/base.html' %}
{% load render_table from django_tables2 %}
{% block content %}
<h1>Projects List</h1>
{% render_table table %}
{% endblock content %}
urls.py
from django.urls import path
from . import views
app_name = 'portal'
urlpatterns = [
path('', views.homepage, name='homepage'),
path('password_generator/', views.password_generator,
name='password_generator'),
path('projects_list/', views.projects_list, name='projects_list'),
path('project/<str:project_id>/', views.project_detail,
name='project_detail'),
path('add/', views.project_add, name='project_add'),
]
I found the issue in my code. In my project_add view, I was trying to redirect to "project_list" url but it didnt exists. That was the mistake....