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.
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 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}
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})
views.py
def crave_view(request):
if (request.method=="POST"):
form1=CraveDataForm(request.POST, request.FILES)
form2 = CraveReplyForm(request.POST, request.FILES)
print form1
print form2
if form1.is_valid() and form2.is_valid():
crave_made = form1.save(commit=False)
crave_made.person = request.user
crave_made.save()
reply = form2.save(commit=False)
reply.person=request.user
reply.crave = crave_made
reply.save()
#login(request,crave_made.username,form1.clean_password2())
messages.success(request, 'You Registered Successfully')
#return HttpResponseRedirect('/details/')
else:
print form1
print form2
messages.error(request, 'Please enter all required fields')
else:
form2 = CraveReplyForm()
form1=CraveDataForm()
return render(request, "crave/crave.html", { 'form1' : form1, 'form2' : form2 })
models.py
class crave_data(models.Model):
person = models.ForeignKey(User)
post=models.TextField(blank = True,null = True)
date= models.DateTimeField(auto_now_add=True, blank=True)
def __unicode__(self):
return self.post
class comments(models.Model):
crave=models.ForeignKey(crave_data)
reply=models.CharField(max_length=1000, blank = True,null = True)
date= models.DateTimeField(auto_now_add=True, blank=True)
def __unicode__(self):
return self.reply
forms.py
class CraveDataForm(forms.ModelForm):
class Meta:
model = crave_data
exclude=['date', 'password1', 'password2', 'username', 'person']
class CraveReplyForm(forms.ModelForm):
class Meta:
model = comments
exclude=['date', 'crave', 'password1', 'password2', 'username']
crave.html
<form class="horizontal-form" role="form" action="." method="post" style="padding: 10px;">
{% csrf_token %}
<div class="form-group" >
<div class="col-sm-10">
{{ form1.post.label_tag }}{{ form1.post }} <br /><br>
</div>
</div>
<input type="submit" class="btn btn-success" value="crave" />
</form>
<form class="horizontal-form" role="form" action="." method="post" style="padding: 10px;">
{% csrf_token %}
<div class="form-group" >
<div class="col-sm-10">
{{ form2.reply.label_tag }} {{ form2.reply }} </br> </br>
</div>
</div>
<input type="submit" class="btn btn-success" value="reply" />
</form>
i want to submit data of form one only, but the thing is happening that for form2 i.e. for comment blank object is getting created, its due to a foriegn key i given in comment class.
i dont want the blank object getting created for form2. The data should get saved for form2 after clicking on the submit button of form2. And i can not remove foreign key relationship from there also. reply should get submitted for that relative crave only. and data should get submitted independantlly.
I dont want data to be saved for both form after clicking on submit button of one form.
You shouldn't use one view function for two different actions.
You should do view for add/edit Crave objects like this:
from django.shortcuts import get_object_or_404
from django.core.exceptions import PermissionDenied
from django.contrib.auth.decorators import login_required
#login_required
def crave_edit(request, id=None):
instance = get_object_or_404(crave_data, pk=id) if id else None
if instance and instance.person != request.user:
raise PermissionDenied()
if request.method=="POST":
form=CraveDataForm(request.POST, request.FILES, instance=instance)
if form.is_valid():
crave_made = form.save(commit=False)
crave_made.person = request.user
crave_made.save()
else:
form=CraveDataForm(instance=instance)
return render(request, "crave/crave_edit.html", {'form' : form})
Once again, you shouldn't mix add/edit Crave with add comments. You can't add comment to non-exist entity:
#login_required
def crave_view(request, id):
crave = get_object_or_404(crave_data, pk=id)
if request.method=="POST":
form=CraveReplyForm(request.POST, request.FILES)
if form.is_valid():
reply = form.save(commit=False)
reply.person=request.user
reply.crave = crave
reply.save()
else:
form=CraveReplyForm()
return render(request, "crave/crave_view.html", {'crave': crave, 'form' : form})
And please note that names for models should be capitalised, without underlines - "crave_data" is bad style
You can make the foreign key field optional in Django model
Try this way,
crave=models.ForeignKey(crave_data, blank=True, null=True)
If you want submit the forms independently. then put the hidden input field in one form and separate the form logic in view based on hidden input. Like this,
if 'hidden_input' in request.POST.keys():