tl;dr How to autofill an editable form with information stored in database
Hey, Im creating a profile page for an application using Django as a framework. And Im having some annoying issues when a user is editing their page. As it is now, the user has to retype every field in the form, to edit a single field.. Cause my view has to delete the previous information in each field, or I get some annoying errors.
So my question is, is there a way to autofill these fields in profile_edit.html with the strings corresponding to each field in the form, from the database?
Any help would be greatly appreciated :D
view.py
#login_required
def profile_edit(request):
form = ProfileUpdateForm(request.POST, request.FILES)
if request.method == 'POST':
if form.is_valid():
user = request.user
if 'image' in request.FILES:
user.profile.image = request.FILES['image']
user.profile.bio = form.cleaned_data.get("bio")
user.profile.birth_date = form.cleaned_data.get("birth_date")
user.profile.location = form.cleaned_data.get("location")
user.save()
return redirect('profile')
else:
form = ProfileUpdateForm()
context = {
'form' : form
}
return render(request, 'webside/profile_edit.html', context)
models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(max_length=500, blank=True)
location = models.CharField(max_length=30, blank=True)
birth_date = models.DateField(null=True, blank=True)
email_confirmed = models.BooleanField(default=False)
image= models.FileField(upload_to='profile_image/', blank = True)
def __str__(self):
return self.user.username
profile_edit.html
'{% csrf_token %}
{% for field in form %}
<p>
{{ field.label_tag }}<br>
{{ field }}
{% for error in field.errors %}
<p style="color: red">{{ error }}</p>
{% endfor %}
</p>
{% endfor %}'
pic of profile.html
forms.py
class ProfileUpdateForm(forms.ModelForm):
YEARS= [x for x in range(1900,2021)]
birth_date = forms.DateField( initial="21-06-1995", widget=forms.SelectDateWidget(years=YEARS))
class Meta:
model = Profile
fields = ('bio','birth_date','location','image')
The way you initialise your form in your view is all wrong:
def profile_edit(request):
user = request.user
# form = ProfileUpdateForm(request.POST, request.FILES) <-- remove
if request.method == 'POST':
form = ProfileUpdateForm(request.POST, request.FILES, instance=user.profile)
if form.is_valid():
form.save() # <-- you can just save the form, it will save the profile
# user.save() <-- this doesn't help you, it doesn't save the profile and since user isn't changed you don't need to save it!
return redirect(...)
# else:
# form = ProfileUpdateForm() <-- don't clear the form!
else: # GET
form = ProfileUpdateForm(instance=user.profile) <-- initialise with instance
context = {
'form' : form
}
return render(request, 'webside/profile_edit.html', context)
You need to add the instance to the form to update an existing instance. You shouldn't initialise an empty form if the form is not valid, because that means the user loses all the data if they made a mistake. You want to display the form with all the data and the errors in that case.
Related
I am working on a forum using django, I have a problem accessing user fullname and bio, from a model class I have. I have no problem accessing the user.username or user.email, but not from the Author class..
This is from the models.py in the forum app
User = get_user_model()
class Author(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
fullname = models.CharField(max_length=40, blank=True)
slug = slug = models.SlugField(max_length=400, unique=True, blank=True)
bio = HTMLField()
points = models.IntegerField(default=0)
profile_pic = ResizedImageField(size=[50, 80], quality=100, upload_to="authors", default=None, null=True, blank=True)
def __str__(self):
return self.fullname
My form is in the user app, where i have a profile update site, and the form is like this
from forums.models import Author
class UpdateForm(forms.ModelForm):
class Meta:
model = Author
fields = ('fullname', 'bio', 'profile_pic')
Then here is some of the update site, however nothing let me get access to the bio or fullname, I've tried so many combos. and I am lost here..
{% block content %}
<section class="section" id="about">
<!-- Title -->
<div class="section-heading">
<h3 class="title is-2">Hey {{ user.username }}</h3>
<div class="container">
<p>{{ user.bio }}bio comes here</p>
</div>
</div>
Here is the view.py from the user app
from .forms import UpdateForm
def update_profile(request):
context = {}
user = request.user
instance = Author.objects.filter(user=user).first()
if request.method == 'POST':
form = UpdateForm(request.POST, request.FILES, instance=user)
if form.is_valid():
form.instance.user = user
form.save()
return redirect('/')
else:
form = UpdateForm(instance=user)
context.update({
'form': form,
'title': 'update_profile',
})
return render(request, 'register/update.html', context)
The form html
<form method="POST" action="." enctype="multipart/form-data">
{% csrf_token %}
{{form|crispy}}
<hr>
<button class="button is-block is-info is-large is-fullwidth">Update <i class="fa fa-sign-in" aria-hidden="true"></i></button>
</form>
If there is some relation i am missing please help
Your view currently creates a new Author record each time you "update" the model. I would advise to first clean up the database and remove all authors.
Then you can convert the ForeignKey into a OneToOneField here: that way we know that each user has at most one Author:
from django.conf import settings
class Author(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE
)
# …
Now we can alter the view to create a record in case there is no such record, or update an existing record if there is one:
from .forms import UpdateForm
def update_profile(request):
context = {}
user = request.user
instance = Author.objects.filter(user=user).first()
if request.method == 'POST':
form = UpdateForm(request.POST, request.FILES, instance=instance)
if form.is_valid():
form.instance.user = user
form.save()
return redirect('/')
else:
form = UpdateForm(instance=instance)
context.update({
'form': form,
'title': 'update_profile',
})
return render(request, 'register/update.html', context)
In the template, you can render data of the related Author model for a user user with:
{{ user.author.fullname }}
{{ user.author.bio }}
I am trying to create a django website similar to that of Udemy or Coursera. I am trying to implement a feature that lets users add courses.
Here is my view for adding the course:
def create_course(request):
form = CreateCourseForm()
if request.method == 'POST':
form = CreateCourseForm(request.POST)
if form.is_valid():
new_course = form.save(commit=False)
new_course.instructor = request.user
new_course.save()
return redirect('home')
return render(request, 'courses/create.html',{'form':form})
Here is my form:
class CreateCourseForm(forms.ModelForm):
class Meta:
model = Course
fields = ('name','video','description','category',)
My course model:
class Course(models.Model):
name = models.CharField(max_length=200)
instructor = models.ForeignKey(User, on_delete=models.CASCADE)
video = models.FileField(upload_to='videos/')
description = models.TextField()
category = models.ForeignKey(Category, on_delete=models.CASCADE)
def __str__(self):
return self.name
And finally my web form:
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<h1>Create a Course</h1>
<hr>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form|crispy }}
<button class="btn btn-sm btn-outline-primary" type="submit">Create Course</button>
</form>
{% endblock %}
The issue i am facing is that when i try to create a course, the video field shows an error "This field is required" even when I have uploaded a video.
I tried researching a bit and found out that adding enctype="multipart/form-data" to the form was required, but even adding that didn't solve my problem
Can anyone help me here?
You need to pass request.POST and request.FILES to the form, so:
def create_course(request):
form = CreateCourseForm()
if request.method == 'POST':
# add request.FILES ↓
form = CreateCourseForm(request.POST, request.FILES)
# …
return render(request, 'courses/create.html',{'form':form})
If you only pass request.POST, you are only passing the items of the form that are not file uploads. This then will indeed add an error to the form fields of these files, saying that the form requires data for these fields.
as willem told you have to request file in you form like this
def create_course(request):
form = CreateCourseForm()
if request.method == 'POST':
form = CreateCourseForm(request.POST, request.FILES)
if form.is_valid():
new_course = form.save(commit=False)
new_course.instructor = request.user
new_course.save()
return redirect('home')
return render(request, 'courses/create.html',{'form':form})
but i think you have to know one more you don't have to pass / in while telling django where to upload that file
class Course(models.Model):
name = models.CharField(max_length=200)
instructor = models.ForeignKey(User, on_delete=models.CASCADE)
video = models.FileField(upload_to='videos')
description = models.TextField()
category = models.ForeignKey(Category, on_delete=models.CASCADE)
def __str__(self):
return self.name
offcourse willem answer will solve your problem but you will see in your media folder a new folder name videos/ got created where you file is storing instead of your actual folder becuase you are telling django to upload that file in folder name vedios/ and if django doesn't find that folder it will create that folder with that name and then stat uploading it
I am new to Django and i am working on a website where user can submit a post. Django Form is not saving in database when i have manytomany field in model. I do not know if i can achieve this in Django, I want to attached other user names to the post so that when i submit the form the user name is selected automatically when i check on the post in admin. I will attach a screenshots for clarity.
This image below is my form, as you can see 'My post' is the image_caption while 'andy' is another user name, i want 'andy' selected automatically in manytomany field when form is submitted.
This is what i want when the form is submitted then i check in admin. The other user name (andy) is selected in manytomany field when the form is submitted. I did this manually
Model:
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,blank=True,null=True)
profile_pic = models.ImageField(upload_to='ProfilePicture/', default="ProfilePicture/user-img.png", blank=True)
class Post(models.Model):
poster_profile = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,blank=True,null=True)
image_caption = models.TextField(blank=True, null=True)
tag_someone = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='tagged_users', blank=True)
Forms:
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = (
'image_caption',
'tag_someone',
)
Views:
def upload_view(request):
ImageFormset = modelformset_factory(File, fields=('files',), extra=20)
if request.method == "POST":
form = PostForm(request.POST)
formset = ImageFormset(request.POST, request.FILES)
if form.is_valid() and formset.is_valid():
post = form.save(commit=False)
post.poster_profile = request.user
post.save()
form.save_m2m()
for f in formset:
try:
photo = File(post=post, files=f.cleaned_data['files'])
photo.save()
except Exception as e:
break
return redirect('/')
else:
form = PostForm()
formset = ImageFormset(queryset=File.objects.none())
#User Name Auto-complete In Tag Form
all_users = User.objects.values_list('username', flat=True)
context = {
'form': form,
'formset': formset,
'all_users': all_users,
}
return render(request, 'upload.html', context)
Upload.html:
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form }}
{{ formset }}
<button type="submit" class="btn btn-primary btn-sm btn-block w-25">Post</button>
</form>
I was able to get this working by changing widget to:
widget = {
forms.Select(),
}
You can change the widget here. Widgets are the thing that is responsible for outputting the inputs to HTML. So you just need:
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = (
'image_caption',
'tag_someone',
)
widgets = {'tag_someone': forms.TextInput}
Hello as I posted here before I new to django python need some help on how to get objects of a user that is currently logged in. I have an app named stores which contains a models named Store. I just want to gather the list of stores a user created. Do I have to add anything in setting.py ?
I am really confused about it I also tried request.user and tried to filter the object but I wasn't able to do it. I am posting my code over here kindly look into it and do let me know.
Do let me know if you need anything else to understand.
views.py of stores app
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from .forms import NewStore
from .models import Store
def store(request):
form = NewStore()
if request.method == 'POST':
form = NewStore(request.POST)
if form.is_valid():
form = form.save()
return redirect('stores_list')
else:
form = NewStore()
return render(request, "default/store.html", {'form': form})
#login_required()
def stores_list(request):
my_stores = Store.objects.all()
print(my_stores)
return render(request, "default/stores_list.html", {'my_list': my_stores})
models.py of stores app
from django.db import models
from django.contrib.auth import get_user_model
User = get_user_model()
class Store(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=30)
lat = models.DecimalField(max_digits=50, decimal_places=20)
long = models.DecimalField(max_digits=50, decimal_places=20)
type = models.CharField(max_length=30)
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
def __str__(self):
return self.name
stores_list.html
{% extends 'default/base.html' %}
<html>
<head><title>E-Commerce App</title></head>
{% block content %}
<h1>Welcome to your profile</h1>
<h2>Stores List</h2>
<p>This is your store list</p>
<ul>
{% for s in my_list %}
<li> Store {{ s.id }}: {{ s.name }} </li>
{% endfor %}
</ul>
{% endblock %}
</html>
Change this
if request.method == 'POST':
form = NewStore(request.POST)
if form.is_valid():
form = form.save()
return redirect('stores_list')
to
if request.method == 'POST':
form = NewStore(request.POST)
if form.is_valid():
entry = form.save(commit=False)
# Attach logged-in user details with the 'store' entry
entry.user = request.user
entry.save()
return redirect('stores_list')
To get the store list, created under the logged in user, use
logged_in_user = request.user
store_list = Store.objects.filter(user=logged_in_user)
You need to filter all stores for those belonging to the current user:
#login_required()
def stores_list(request):
my_stores = Store.objects.all().filter(user=request.user)
print(my_stores)
return render(request, "default/stores_list.html", {'my_list': my_stores})
# models.py
class Profile(models.Model):
name = models.CharField(max_length=255)
image = models.ImageField(upload_to=image_path, blank=True, null=True)
# forms.py
class ProfileForm(ModelForm):
class Meta:
model = Profile
fields = '__all__'
# views.py
def profile(request, id):
p = get_object_or_404(Profile, pk=id)
profile_form = ProfileForm(request.POST or None,
files=request.FILES or None,
instance=p)
if request.method == 'POST':
if profile_form.is_valid():
profile_form.save()
return render(request, 'profile.html', {'form': profile_form})
#profile.html
<form method="post" action="" enctype="multipart/form-data" >
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit" />
</form>
If a user GETs /profile/1 and 1 exists, they get the form prefilled with all the values, including the image (if any)
If the user then POSTs updates to any of the fields except the image (clearing or changing), The page renders correctly after the update.
However, if the user changes the Image (clear or change), the change is not reflected in the rendered result: The image is always shown as empty. a quick GET solves the situation and displays the changed situation.
Does anyone know why the image field doesn't show the update in this situation?
Don't you have to do some sort of redirection to somewhere(obviously with a GET) when the profile form is successfully saved? You were always returning render.... and if you are having a POST request the form is going to stay with the POST data. So it should be:
# views.py
def profile(request, id):
p = get_object_or_404(Profile, pk=id)
profile_form = ProfileForm(request.POST or None, files=request.FILES or None, instance=q)
if request.method == 'POST':
if profile_form.is_valid():
profile_form.save()
# do the GET request to some page
return redirect('some-view-name', foo='bar')
return render(request, 'profile.html', {'form': profile_form})