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}
Related
I'm creating an Instagram-style application with Django 3.2.5, where users have profiles to visit and upload photos. I am implementing a commenting system.
When generating the comment as a user with a registered profile, the comment is rendered as if it were anonymous.
I have investigated and it happens that in the model of the comment I made a relationship with the user and profile giving the null parameter as true.
When removing this parameter and resubmitting a comment on a post I get a Not Null Constraint Failed error for profile_id and for user_id.
The image shows the first comments made from the admin. And the last one with the null = true parameter in the model.
Sample picture
models.py
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
user = models.ForeignKey(User, on_delete=models.CASCADE)
profile = models.ForeignKey(Profile, on_delete=models.CASCADE)
text = models.TextField(max_length=200)
created_date = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['created_date']
def __str__(self):
return self.text
views.py
def create_comment(request, pk):
post = get_object_or_404(Post, pk=pk)
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.post = post
comment.save()
return redirect('posts:feed')
else:
form = CommentForm()
return render(request, 'posts/feed.html', {
'form': form,
})
urls.py
urlpatterns = [
path(r'^post/(?P<pk>[0-9]+)/comment/$', create_comment, name='create'),]
forms.py
class CommentForm(forms.ModelForm):
pass
class Meta:
model = Comment
fields = ['text']
HTML
<form action="{% url 'comments:create' pk=post.pk %}" method="POST" >
{% csrf_token %}
<div class="input-group">
<input type="text" class="form-control" placeholder="Comment here" aria-label="text" name="text" value="{{ post.comment.pk}}" >
<div class="input-group-append">
<button class="btn btn-dark" type="submit" id="button-addon2"><i class="fas fa-comment"></i></button>
</div>
</div>
</form>
You should link the .user attribute to the logged in user, so:
from django.contrib.auth.decorators import login_required
#login_required
def create_comment(request, pk):
if request.method == 'POST':
form = CommentForm(request.POST, request.FILES)
if form.is_valid():
form.instance.post_id = pk
form.instance.user = request.user
form.save()
return redirect('posts:feed')
else:
form = CommentForm()
return render(request, 'posts/feed.html', {'form': form})
By making use of .post_id = pk, we no longer query the database to obtain the post first, validation is done when creating the Comment instance.
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
Note: You can limit views to a view to authenticated users with the
#login_required decorator [Django-doc].
I currently have a model that is called services, shown here...
class Services(models.Model):
service_sku = models.AutoField(primary_key=True)
name = models.CharField(max_length=200, null=True)
price = models.FloatField(null=True)
forms.py
class ServiceForm(forms.ModelForm):
class Meta:
model = Services
fields = '__all__'
Based on the code shown above, I have two views, one will create instances of the model above and the other view will update the instance, as follows
def NewServices(request):
form = ServiceForm()
if request.method == 'POST':
form = ServiceForm(request.POST or None)
if form.is_valid():
form.save()
return redirect('/')
return render(request, 'accounts/new_services.html', {'form': form})
def EditServices(request,pk):
service = Services.objects.get(service_sku=pk)
form = ServiceForm(instance=service)
if request.method == 'POST':
form = ServiceForm(request.POST, instance=service)
if form.is_valid():
form.save()
return redirect('/')
context = {'form':form}
return render(request, 'accounts/edit_services.html',context)
Template as follows
<div class="row">
<div class="col-md-7">
<div class="card card-body">
<form action="" method="POST">
{% csrf_token %}
{{form}}
<hr>
<input type="submit" name="Update">
</form>
</div>
</div>
</div>
Is it possible to show a readyonly of the service_sku within in my template form, when a instance needs to be updated and when a service_sku readonly is shown to be autogenerated in a template when creating a instance?
Yes. You can include the field in a form, so likely you should make a separate one to update:
class ServiceEditForm(forms.ModelForm):
service_sku = forms.IntegerField(disabled=True)
class Meta:
model = Services
fields = ('service_sku', 'name', 'price')
In the form you then simply use that new form:
def EditServices(request,pk):
service = Services.objects.get(service_sku=pk)
if request.method == 'POST':
form = ServiceEditForm(request.POST, request.FILES, instance=service)
if form.is_valid():
form.save()
return redirect('/')
else:
form = ServiceEditForm(instance=service)
context = {'form':form}
return render(request, 'accounts/edit_services.html', context)
Using disabled=True [Django-doc] does not only make sure the field is disabled at the client side, but it will also prevent a person to make a malicious POST request.
as my first project in Django I am creating a todo app, that lets people log in and see their own tasks that they created. For that, I need to save author info in single task data.
From what I learned reading the documentation and doing lots of google-searching, the current approach is to use the get_user_model function from django.contrib.auth.
The problem is, that whenever I try to use it in my model, it seems to not get the username from the currently logged in user. While printing the form.errors to my console, the output is:
<ul class="errorlist"><li>added_by<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
Seems like the get_user_model is not returning any value. Can anyone recommend a better approach for me to do that? Or is there something obvious that I missed?
Here are the code snippets:
models.py
from django.db import models
from django.contrib.auth import get_user_model
class Task(models.Model):
title = models.CharField(max_length=35)
completed = models.BooleanField(default=False)
created_date = models.DateTimeField(auto_now_add=True)
added_by = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
def __str__(self):
return self.title
forms.py
from django import forms
from .models import *
class TaskForm(forms.ModelForm):
class Meta:
model = Task
fields = '__all__'
widgets = {
'title': forms.TextInput(attrs={'class': 'new_task_text', 'placeholder': 'Add new task'}),
}
views.py
#login_required
def list_homepage(request):
tasks = Task.objects.all()
form = TaskForm()
if request.method == 'POST':
form = TaskForm(request.POST)
if form.is_valid():
form.save()
print(form.cleaned_data)
else:
print(form.errors)
return redirect('/list/home')
context = {
'page_title': 'Todo list',
'tasks': tasks,
'form': form,
}
return render(request, 'tasks/list.html', context)
form in template:
<form method="POST", action="#"> {% csrf_token %}
{{ form.title }}
<input class="submit" type="submit", value="Create task">
</form>
Thank you in advance for any help!
Your form template only includes the title field, you either need to add the added_by field to this form, or add it in the view handling
{{ form.added_by }}
or
if request.method == 'POST':
form = TaskForm({**request.POST, **{"added_by": request.user}})
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.
# 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})