How to save changed fields with ModelForm? - python

I want to save changed values of ModelForm to database. I'm having problems even I follow this documentation if I'm right that it can be possible with initial values: Documentation- providing initial values
models.py:
class Settings(models.Model):
url = models.URLField(max_length=100, default='https://website.com')
maxCount = models.SmallIntegerField(default=30)
views.py:
def Settings(request):
settingsObj = Settings.objects.get(id=1)
form = SettingsForm(initial={'url': settingsObj.url, 'maxCount':settingsObj.maxCount}, instance=settingsObj)
if form.is_valid():
form.save()
forms.py:
class SettingsForm(forms.ModelForm):
class Meta:
model = Settings
fields = ['url', 'maxCount']
templates
<form class="form-horizontal" role="form" method="POST">{% csrf_token %}
<div class="form-group">
<div class="col-sm-offset-1 col-sm-6">
{{ form.as_p }}
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-1 col-sm-6">
<button type="submit" class="btn btn-primary btn-lg btn-block">Accept</button>
</div>
</div>
</form>
Currently the form is showing the current values from database, but isn't saving changed data. form.is_valid() returns True, but form.save() seems to do nothing.

The initial argument and the instance argument in your call to SettingsForm() serve the exact same purpose, because you are using the fields of the instance individually in each field of initial.
The save() method is not working because you need to populate the form with data from request.POST.
This view should work:
def settings(request):
settingsObj = Settings.objects.get(id=1)
if request.POST:
form = SettingsForm(request.POST, instance=settingsObj)
if form.is_valid():
form.save()
else:
form = SettingsForm(instance=settingsObj)
context = { ..., 'form': form, ... }
return render(request, 'template-address', context)

Related

Django Form, form is not being submitted

I am pretty new to django and I am having some issues with my form. It is not submitting anything. I don´t have any idea why, no issue appears in the terminal. It displays the form correctly, but when filling it out and submitting, it just redirects me to the same form but blank. I check the database and nothing´s been added. My code below:
#views.py
def ContractView(request):
form=contractsform(request.POST)
if form.is_valid():
con =form.save()
return redirect("{% url 'contracts' %}", con.id)
else:
form = contractsform()
return render(request, 'contform.html', {'form': form})
#contform.html
<div class="card-body">
<form action="" method="POST" class="row g-3">
{% csrf_token %}
<label for="{{ form.subject.id_for_label }}">Name:</label>
{{form.name}}
<div class="col-md-6">
<label for="{{ form.subject.id_for_label }}">Contractor:</label>
<div class="input-group input-group-sm mb-3">
{{form.contractor}}
<button id="new-vendor" class="btn btn-outline-secondary" type="button">+</button>
</div>
</div>
<div class="col-md-6">
<label for="{{ form.subject.id_for_label }}">Contractee:</label>
{{form.contractee}}
</div>
...
<div class="col-md-6">
<button type="button" onclick="javascript:history.back()">Cancel</button>
</div>
<div class="col-md-6">
<input type="submit" value="Submit" class="btn btn-primary" style="float: right;">
</div>
</form>
#forms.py
class contractsform(forms.ModelForm):
class Meta:
model = Contratos
fields = '__all__'
widgets = {
'name': forms.TextInput(attrs ={'class': 'form-control'}),
'contractee': forms.TextInput(attrs={'class': 'form-control'}),
'contractor': forms.Select(attrs={'class': 'form-control', 'id': 'contractor_view' }),}
#urls.py
urlpatterns = [
path('contracts/', views.contratostabla, name='contracts'),
path('contracts/add/', ContractView, name='new-contract'),
]
You are currently redefining the form as an empty form when it is not valid, you need to change you logic to use a form filled with POST data when the request method is POST and an empty form when the method is GET. You still need to use the filled form for POST when it's invalid so that you get the errors and the previous data
def ContractView(request):
if request.method == 'POST':
form = contractsform(request.POST)
if form.is_valid():
con = form.save()
return redirect("{% url 'contracts' %}", con.id)
else:
form = contractsform()
return render(request, 'contform.html', {'form': form})
FYI on readability, the convention is to use CamelCase for classes so your form would be named ContractsForm and lowercase-with-underscores for functions so your view would be contract_view
You can update your view like this
def ContractView(request):
form = contractsform(request.POST or None)
if form.is_valid():
con = form.save()
con.save()
return redirect("{% url 'contracts' %}", con.id)
else:
form = contractsform()
return render(request, 'contform.html', {'form': form})
I think this will solve your problem

How to render user profile (actual image) instead of filename in a Django form which basically update user-account

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>

Django and MySQL save multiple data

I am trying to save multiple fields of data. I've also changed the database connection from the default sqlite3 to MySQL. And I don't know how to do this
Here's my views.py
def customerview(request):
if request.POST:
form = CustomerForm(request.POST)
if form.is_valid():
if form.save():
return redirect('sales')
else:
return redirect('index')
else:
return redirect('index')
else:
form = CustomerForm
return render(request, 'customer.html', {'form':form})
def salesview(request):
if request.POST:
form = SalesForm(request.POST)
if form.is_valid():
if form.save():
return redirect('index')
else:
return redirect('index')
else:
return redirect('index')
else:
form = SalesForm
data = Customer.objects.latest('id')
return render(request, 'sales.html', {'form':form, 'range':range(data.number_of_transactions)})
Here's my models.py
class Customer(models.Model):
customer_name = models.CharField(max_length=200)
number_of_transactions = models.IntegerField()
class Sales(models.Model):
product_type = models.CharField(max_length=100)
product_code = models.CharField(max_length=100)
product_size = models.CharField(max_length=100)
product_quantity = models.IntegerField()
Here's my brands.html
<form class="form" role="form" action="" method="post"> {% csrf_token %}
{% for i in range %}
<div class="col">
<div class="col-sm-3">
<div class="">
{{ form.product_type | add_class:'form-control' }}
<label for="regular2">Product Type</label>
</div>
</div>
<div class="col-sm-3">
<div class="">
{{ form.product_code | add_class:'form-control' }}
<label for="regular2">Product Code</label>
</div>
</div>
</div>
{% endfor %}
<div class="col-md-12">
<hr>
<div class="card-actionbar-row">
<input type="submit" class="btn btn-flat btn-primary ink-reaction" value="SUBMIT">
</div>
</div>
</form>
The idea is to get the customer details and number of transactions to be performed then that determines the number of fields to be displayed in the sales view. And that works fine.
The problem is to get each of the transactions to be saved in the database. When I submit and check my database tables, only one transaction is saved.
It's clear that you're trying to run before you can walk here.
Firstly, you should concentrate on getting a simple list view to work, without getting confused about the additional complexity involved in displaying a list in a form view. So, make your view inherit from ListView, and remove all the methods. Then fix your template, so that it iterates over stock_list or object_list rather than just stock.
Secondly, once you've got that working, you could try to integrate it with a form. When you do that, learn what methods to override. get_queryset must return a queryset, it should not render a template. In any case, you should almost never need to render a template manually in a class-based view, because the existing logic will do that for you. And if you want to add a queryset to the template context in a create view, for example, you should be overriding get_context_data; which needs to return a dictionary.
Thirdly, if at some point you do need to render a template manually, read the documentation to learn the order of parameters to render: it is request, template_name, context, not as you have it.

Django Model Form not appearing in admin

I've got a feedback app in django and it all seems to work fine, no errors i can submit the form and it all seems to work, however i have my model registered into my admin however when i submit the form i doesn't appear in my admin. Sorry if this is very basic i just cant get my head around it please help.
in my models.py
class Feedback(models.Model):
email = models.CharField(max_length=100)
message = models.CharField(max_length=1000)
def __unicode__(self):
return self.title
which i then pass through to forms.py
class FeedbackModelForm(forms.ModelForm):
class Meta:
model = Feedback
fields = ["email", "message"]
and my view is
def feedbackform(request):
form = FeedbackModelForm(request.Post or None)
if form.is_valid():
form.save()
return render(request, "feedback.html", {"form": form})
now in my html looks like this
{% block content %}
<div id="feedback">
<div id="feedback-form" style='display:none;' class="col-xs-4 col-md-4 panel panel-default">
<form method="POST" action="{{ form }}" class="form panel-body" role="form">{% csrf_token %}
<div class="form-group">
<input class="form-control" name="email" autofocus placeholder="Your e-mail" type="email" />
</div>
<div class="form-group">
<textarea class="form-control" name="message" required placeholder="Please write your feedback here..." rows="5"></textarea>
</div>
<button class="btn btn-primary pull-right" type="submit">Send</button>
</form>
</div>
<div id="feedback-tab">Feedback</div>
</div>
{% endblock %}
and in my admin
from .models import Feedback
from .forms import FeedbackModelForm
class FeedbackAdmin(admin.ModelAdmin):
form = FeedbackModelForm
admin.site.register(Feedback, FeedbackAdmin)
You have passed the
{{ form }}
as the action attribute, which is completely wrong. Put it inside a div as
{{ form.as_p }}
that will work for you.
And in the action attribute pass a url in the form of
{% url 'home_page_example' %}
if you wanted to remain in the same page and redirect via view
you can write
action = "."
Show us how did you register your model in the admin.
Make sure that you explicit config the form, like this
class FeedbackAdmin(admin.ModelAdmin)
form = FeedbackModelForm
admin.site.register(Feedback, FeedbackAdmin)
You should return email or message in def __unicode__(self):, not title.
class Feedback(models.Model):
email = models.CharField(max_length=100)
message = models.CharField(max_length=1000)
def __unicode__(self):
return self.email
I think that you should check if the view is currently saving your Feedback.
Try inspecting the DB or in a manage.py shell check if len(Feedback.objects.all()) change when you submit a Feedback in your view.
Also, I recommend you to change the email field to an EmailField and use the FormView class based view.

How can I build multiple submit buttons django form?

I have form with one input for email and two submit buttons to subscribe and unsubscribe from newsletter:
<form action="" method="post">
{{ form_newsletter }}
<input type="submit" name="newsletter_sub" value="Subscribe" />
<input type="submit" name="newsletter_unsub" value="Unsubscribe" />
</form>
I have also class form:
class NewsletterForm(forms.ModelForm):
class Meta:
model = Newsletter
fields = ('email',)
I must write my own clean_email method and I need to know by which button was form submited. But the value of submit buttons aren't in self.cleaned_data dictionary.
Could I get values of buttons otherwise?
Eg:
if 'newsletter_sub' in request.POST:
# do subscribe
elif 'newsletter_unsub' in request.POST:
# do unsubscribe
You can use self.data in the clean_email method to access the POST data before validation. It should contain a key called newsletter_sub or newsletter_unsub depending on which button was pressed.
# in the context of a django.forms form
def clean(self):
if 'newsletter_sub' in self.data:
# do subscribe
elif 'newsletter_unsub' in self.data:
# do unsubscribe
You can also do like this,
<form method='POST'>
{{form1.as_p}}
<button type="submit" name="btnform1">Save Changes</button>
</form>
<form method='POST'>
{{form2.as_p}}
<button type="submit" name="btnform2">Save Changes</button>
</form>
CODE
if request.method=='POST' and 'btnform1' in request.POST:
do something...
if request.method=='POST' and 'btnform2' in request.POST:
do something...
one url to the same view!
like so!
urls.py
url(r'^$', views.landing.as_view(), name = 'landing'),
views.py
class landing(View):
template_name = '/home.html'
form_class1 = forms.pynamehere1
form_class2 = forms.pynamehere2
def get(self, request):
form1 = self.form_class1(None)
form2 = self.form_class2(None)
return render(request, self.template_name, { 'register':form1, 'login':form2,})
def post(self, request):
if request.method=='POST' and 'htmlsubmitbutton1' in request.POST:
## do what ever you want to do for first function ####
if request.method=='POST' and 'htmlsubmitbutton2' in request.POST:
## do what ever you want to do for second function ####
## return def post###
return render(request, self.template_name, {'form':form,})
/home.html
<!-- #### form 1 #### -->
<form action="" method="POST" >
{% csrf_token %}
{{ register.as_p }}
<button type="submit" name="htmlsubmitbutton1">Login</button>
</form>
<!--#### form 2 #### -->
<form action="" method="POST" >
{% csrf_token %}
{{ login.as_p }}
<button type="submit" name="htmlsubmitbutton2">Login</button>
</form>
It's an old question now, nevertheless I had the same issue and found a solution that works for me: I wrote MultiRedirectMixin.
from django.http import HttpResponseRedirect
class MultiRedirectMixin(object):
"""
A mixin that supports submit-specific success redirection.
Either specify one success_url, or provide dict with names of
submit actions given in template as keys
Example:
In template:
<input type="submit" name="create_new" value="Create"/>
<input type="submit" name="delete" value="Delete"/>
View:
MyMultiSubmitView(MultiRedirectMixin, forms.FormView):
success_urls = {"create_new": reverse_lazy('create'),
"delete": reverse_lazy('delete')}
"""
success_urls = {}
def form_valid(self, form):
""" Form is valid: Pick the url and redirect.
"""
for name in self.success_urls:
if name in form.data:
self.success_url = self.success_urls[name]
break
return HttpResponseRedirect(self.get_success_url())
def get_success_url(self):
"""
Returns the supplied success URL.
"""
if self.success_url:
# Forcing possible reverse_lazy evaluation
url = force_text(self.success_url)
else:
raise ImproperlyConfigured(
_("No URL to redirect to. Provide a success_url."))
return url
I know this is old, but some of the answers are, to say the least, brief, and they do not address a common case where the form is not a django form.
This solution was inspired by this blog post. It relies on using a view class that is derived from django.views.generic.edit.FormMixin, e.g. CreateView, UpdateView or DeleteView. These provide the get_success_url method which exposes the button name in request
html
<html>
<body>
<form method="post">
<div>
<label> <input type="radio" name="select-type" value="A">Type A</label>
</div>
<div>
<label> <input type="radio" name="select-type" value="B">Type B</label>
</div>
<div>
<input type="submit" value="Use selected">
</div>
<div>
<input type="submit" name="no-selection" value="None of the above">
</div>
</form>
</body>
</html>
views.py
from django.views.generic import UpdateView
class GetType(UpdateView):
def get(self, request):
return render(request, 'get_type.html', {})
def post(self, request):
button = self.get_success_url()
print(button)
def get_success_url(self):
if 'no-selection' in self.request.POST:
return 'none selected'
return ''

Categories