My problem is similar to how to edit model data using django forms, but I'm not able to solve it.
I would like to get an form with prefielled fields and to allow user to edit them.
I believe my problem is in views.py file, but unfrotuntely I'm not able to solve it.
models.py
from django.db import models
class Item(models.Model):
product = models.CharField(max_length=150)
quantity = models.DecimalField(max_digits=8, decimal_places=3)
price = models.DecimalField(max_digits=7, decimal_places=2)
purchase_date = models.DateTimeField()
warranty = models.DecimalField(max_digits=4, decimal_places=1)
comment = models.TextField()
forms.py
from django import forms
from items.models import Item
class EditItemForm(forms.ModelForm):
class Meta:
model = Item
fields = ('product','quantity', 'price', 'purchase_date', 'warranty', 'comment')
urls.py
from django.conf.urls import patterns, include, url
urlpatterns = patterns('',
url(r'^show_all/$', 'items.views.items'),
url(r'^(?P<item_id>\d+)/$', 'items.views.item'),
url(r'^edit/(?P<item_id>\d+)/$', 'items.views.edit'),
)
edit.html
<form action="/items/edit/" method="post" class="form horizontal well">{% csrf_token %}
{{ form.as_p }}
<imput type="submit" class="btn btn-inverse" value="Aktualizuj">
</form>
views.py
from django.shortcuts import render_to_response
from django.http import HttpResponseRedirect
from items.models import Item
from decimal import Decimal
from django.core.context_processors import csrf
from items.forms import EditItemForm
def edit(request):
if request.method == 'POST':
form = EditItemForm(request.POST, instance=request.item)
if form.is_valid():
form.save()
return HttpResponseRedirect('/items/show_all/')
else:
form = EditItemForm(instance=item)
args = {}
args.update(csrf(request))
args['form'] = form
return render_to_response('edit.html', args)
Above code is resulting this message:
TypeError at /items/edit/1/
edit() got an unexpected keyword argument 'item_id'
Can you please help me?
Django 1.6, Python 3.4
You've imagined an attribute called request.item. There's no such thing. You need to get the item from the database, via the ID passed into the function as alecxe showed.
def edit(request, item_id):
item = Item.objects.get(pk=item_id)
if request.method == 'POST':
form = EditItemForm(request.POST, instance=item)
edit() view should allow a keyword argument item_id:
def edit(request, item_id=None):
if request.method == 'POST':
...
Related
I have a small problem with adding data to the database in django 2.0.3
I created the following model:
from django.contrib.auth.models import User
class UserInputSignal(models.Model):
name = models.CharField(max_length=512)
author = models.ForeignKey(User, on_delete=models.CASCADE)
input_file = models.FileField(upload_to='signals/', null=True)
I tried to solve the problem using this form:
from django import forms
from .models import UserInputSignal
class UserInputSignalForm(forms.ModelForm):
name = forms.CharField()
input_file = forms.FileField()
class Meta:
model = UserInputSignal
fields = ('name', 'input_file', )
and this view:
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from django.contrib.auth import authenticate
from .forms import UserInputSignalForm
#login_required
def storage(request):
form = UserInputSignalForm(request.POST or None)
if request.method == 'POST':
if form.is_valid():
name = request.POST.get('name')
author = request.POST.get(request.user)
input_file = request.POST.get('input_file')
return redirect('home')
else:
form = UserInputSignalForm()
return render(request, 'storage.html', {'form': form})
In the template I called, I created the form as follows:
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Upload</button>
</form>
I am able to load a page with a form, but it does not post data to the database. I would like to add that I am a novice in django and some mechanisms are just plain understandable for me. Can I ask someone for help with this problem?
Before the redirect, call form.save()
Okay, i worked on your code and it works with me with slight modifications:
models.py
class UserInputSignal(models.Model):
name = models.CharField(max_length=512)
author = models.ForeignKey(User, on_delete=models.CASCADE)
input_file = models.FileField(upload_to='signals/', null=True)
objects = models.Manager()
#this returns the name for your modelobject
def __str__(self):
return self.name
forms.py
#excluded the assiging as fields defination is enough in itself
class UserInputSignalForm(forms.ModelForm):
class Meta:
model = UserInputSignal
#this will exclude the formfield it self but the author will be saved as the person who is logged in
exclude = ["author"]
Edited - Views.py
#login_required
def storage(request):
#authentication for author field using request.user
insta = UserInputSignal(author=request.user)
print(request.user)
form = UserInputSignalForm(request.POST or None, request.FILES or None,instance=insta)
if request.method == 'POST':
if form.is_valid():
signal = form.save(commit=False)
signal.save()
return redirect('home')
else:
form = UserInputSignalForm(instance=insta)
return render(request, 'storage.html', {'form': form})
JlucasRs was right to tell you to use form.save(), but you needed to assign form to something and need not use model fields here as forms.py does that for you.
app/Urls.py - Just for reference
urlpatterns = [
path('home/', home, name='home'),
path('storage/', storage, name='storage'),
]
Edit- Admin.py
from .models import PostModel, UserInputSignal
class UserInputSignalAdmin(admin.ModelAdmin):
list_display = ('name', 'author', 'input_file' )
admin.site.register(UserInputSignal, UserInputSignalAdmin)
Add this code in Admin.py if its not there.
I was reading django docs and found the add_fields method.
The documentation says:
"If you need to add additional fields to the formset this can be
easily accomplished. The formset base class provides an add_fields
method."
I want to use it but there is no example or explanation how
it can be used in views and templates. Can you provide me a small
example of using this method?
I will give a small example.
The models.py looks like this,
from django.db import models
class Article(models.Model):
title = models.CharField(max_length=100)
pub_date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
forms.py looks like this,
from django import forms
from django.forms import BaseFormSet
from .models import Article
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
fields = ['title']
class BaseArticleFormSet(BaseFormSet):
def add_fields(self, form, index):
super().add_fields(form, index)
form.fields['body'] = forms.CharField()
views.py looks like this,
from django.shortcuts import render
from django.forms import formset_factory
from .forms import ArticleForm, BaseArticleFormSet
def home(request):
ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet, extra=3)
context = dict()
formset = ArticleFormSet()
if request.method == 'POST':
formset = ArticleFormSet(request.POST)
if formset.is_valid():
print(formset.cleaned_data)
context['formset'] = formset
return render(request, 'home.html', context)
Finally, home.html will look like this,
<form method="post">
{% csrf_token %}
<table>
{{ formset }}
</table>
<button type="submit">submit</button>
</form>
As you can see, the body field appears in templates but it is not included in the the Article model.
Hope that helps!
Okay, so I'm trying to teach myself django by trying to put together a simple DB query application.
So I have in my DB a relation mysql table storing triples (RDF, subj, obj, pred) and I have written a model form with the fields to query that. Though, I have initially setup my form to store the queries in a separate table. What I would like to do however, is the model form I created to instead query the triple table.
Here is my code:
view:
from django.shortcuts import render, get_object_or_404, render_to_response
from django.template import RequestContext
# Create your views here.
from .forms import QueryForm
from .models import Query
def queries_create(request):
form = QueryForm(request.POST or None)
if form.is_valid():
instance = form.save(commit=False)
instance.save()
context = {
"title":"Create",
"form": form,
}
#return HttpResponse("<h1>create</h1>")
return render(request, "query_form.html", context)
model:
from __future__ import unicode_literals
from django.db import models
from django.core.urlresolvers import reverse
# Create your models here.
class Query(models.Model):
studyName = models.CharField(max_length=250)
population = models.IntegerField()
intervention = models.CharField(max_length=250)
comparison = models.CharField(max_length=250)
outcome = models.CharField(max_length=250)
outcomeTiming = models.CharField(max_length=250)
updated = models.DateTimeField(auto_now=True, auto_now_add=False)
timestamp = models.DateTimeField(auto_now=False, auto_now_add=True)
def __unicode__(self):
return self.studyName
def get_absolute_url(self):
return reverse("queries:detail", kwargs={"id": self.id})
#return "/queries/%s/" %(self.id)
form:
from django import forms
from .models import Query
class QueryForm(forms.ModelForm):
class Meta:
model = Query
fields = [
"studyName",
"population",
"intervention",
"comparison",
"outcome",
"outcomeTiming",
]
html:
<!--DOCTYPE html -->
<html>
<body>
<h1>Query the Model</h1>
<form method='POST' action=''>{% csrf_token %}
{{ form }}
<input type='submit' value='Query!' />
</form>
</body>
</html>
Any help would be appreciated I've tried several modifications but nothing seems to be working.
You need to handle the data differently for the form. Instead of saving it you need to extract the data and query for matching:
def query_queries(request):
form = QueryForm(request.POST or None)
if form.is_valid():
# this is the same as doing
# Query.objects.filter(studyName=form.cleaned_data['studyName']...)
queries = Query.objects.filter(**form.cleaned_data)
context = {
'queries': queries
}
return render(request, "query_queries.html", context)
So I have created a a dynamic formset that allows the user to add or remove as many forms as they want. Now, I am trying to do custom validation in a field(block_name) in the forms.
It seems like it is working because if the user input doesn't match a certain regex, then the data will not be saved. The problem is that there is no message showing that the input is wrong.
Also, if the user inputs incorrect data and tries to submit the page will redirect to itself and erase all the input data. How can I make the page stay in the same view if the input is wrong and also show error messages?
forms.py:
import re
from django import forms
from django.forms.models import modelformset_factory
from inventory.models import Block
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
# Form for block requests
class BlockForm(forms.ModelForm):
block_name = forms.CharField(required=True, validators= [RegexValidator('^s\d{3}rf\d*b\d*e\d+r\d+w\d*[cgls][abcdex][ed][hv][sbaec][a-d] [a-d][0-7][apfg]a', message="Please enter valid block name", code="invalid_name")])
def __init__(self, *args, **kwargs):
super(BlockForm, self).__init__(*args, **kwargs)
self.empty_permitted = False
class Meta:
model = Block
fields = ['block_name', 'block_derivatives', 'block_subsystems', 'owners']
def clean_block_name(self):
print self.cleaned_data
block_name = self.cleaned_data.get('block_name')
if block_name == "a":
print ("block name is a")
raise forms.ValidationError(
('Please enter a block name'))
return block_name
models.py:
import datetime
import re
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError
from django.db import models
from django.utils import timezone
class Inventory(models.Model):
class Meta:
verbose_name_plural = "Inventories"
inventory_name = models.CharField(max_length=100)
pub_date = models.DateTimeField('date published')
def __str__(self): # __unicode__ on Python 2
return self.inventory_name
def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
class Block(models.Model):
class Meta:
verbose_name_plural = "Request Blocks"
inventory = models.ForeignKey(Inventory, null=True)
block_status = models.CharField(max_length=20, blank=False)
#617block_name = models.CharField(max_length=50, blank=False, null=False, validators=[block_nameTest])
block_name = models.CharField(max_length=50, blank=False, null=False)
block_derivatives = models.CharField(max_length=100)
block_subsystems = models.CharField(max_length=40)
owners = models.CharField(max_length=100)
def __str__(self):
return self.block_name
def block_owners(self):
return str(self.owners)
views.py:
from django.forms.models import modelformset_factory
from django.forms.formsets import formset_factory
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from django.views import generic
from django.http import HttpResponse
from django.shortcuts import render_to_response
from .models import Inventory, Block
from .forms import BlockForm
# Create your views here.
def requests(request, inventory_id):
BlockFormSet = formset_factory(BlockForm, extra=1)
inventory = get_object_or_404(Inventory, pk=inventory_id)
formset = BlockFormSet(request.POST)
if request.method == 'POST':
formset = BlockFormSet(request.POST)
if formset.is_valid():
for form in formset:
print form
form.save()
print "Success"
return HttpResponseRedirect('/inventory/2')
else:
print "Yo, this stuff is not validated"
else:
print "LOLOLOLOL"
return render(request, 'inventory/requests.html', {'inventory': inventory, 'formset': BlockFormSet})
requests.html:
{% block content %}
<div class="requestForm">
<form id="blockForm" class="original" action="{% url 'inventory:requests' inventory.id %}" method="post">{% csrf_token %}
<!-- Add New Row -->
{{formset.management_form}}
{% for form in formset %}
<div class='item'>
<ul>{{ form.as_table}}<ul>
<p style=""><a class="delete" href="#">Delete</a></p>
</div>
{% endfor %}
<p><a id="add" href="#">Add another item</a></p>
<input type="submit" name="submit" value="Request Blocks" id="submitButton">
</form>
</div>
{% endblock %}
You're redirecting after an invalid POST, rather than redisplaying the same form. Even though your redirect is to the same view, it loses the POST data and the form is therefore blank.
Drop the first else block, and let execution fall through to the final render line.
I'm trying to ask a user some additional info while signing up. I'm using django allauth for authorization and authentication. I try to add three more fields during the signup process. If If I run it, it shows me the standard form plus gender field. However, it doesn't seem to really work. How can I save the data? Could someone help? Thank you in advance!
EDITED: if I just use
if form.is_valid():
form.save()
return redirect('/success/')
I get an error:
save() missing 1 required positional argument: 'user'
I'm quite new to django.
I created signups app in the project.
I put this in allauth_settings.py:
ACCOUNT_SIGNUP_FORM_CLASS = 'signups.forms.MySignupForm'
My signups/model.py:
from django.contrib.auth.models import User
from django.db import models
from allauth.account.models import EmailAddress
from allauth.socialaccount.models import SocialAccount
import hashlib
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='profile')
about_me = models.TextField(null=True, blank=True)
timestamp = models.DateTimeField(auto_now_add= True, auto_now=False)
updated = models.DateTimeField(auto_now_add= False, auto_now=True)
GENDER_CHOICES = (
('m', 'Male'),
('f', 'Female'),
)
# gender can take only one of the GENDER_CHOICES options
gender = models.CharField(max_length=1, choices=GENDER_CHOICES,
verbose_name='Gender')
def __unicode__(self):
return self.user.username
class Meta:
db_table = 'user_profile'
def profile_image_url(self):
"""
Return the URL for the user's Facebook icon if the user is logged in via
Facebook, otherwise return the user's Gravatar URL
"""
fb_uid = SocialAccount.objects.filter(user_id=self.user.id, provider='facebook')
if len(fb_uid):
return "http://graph.facebook.com/{}/picture?width=40&height=40".format(fb_uid[0].uid)
return "http://www.gravatar.com/avatar/{}?s=40".format(hashlib.md5(self.user.email).hexdigest())
def account_verified(self):
"""
If the user is logged in and has verified hisser email address, return True,
otherwise return False
"""
if self.user.is_authenticated:
result = EmailAddress.objects.filter(email=self.user.email)
if len(result):
return result[0].verified
return False
User.profile = property(lambda u: UserProfile.objects.get_or_create(user=u)[0])
my signups/forms.py:
from allauth.account.forms import SignupForm
from django import forms
from .models import UserProfile
class MySignupForm(SignupForm):
class Meta:
model = UserProfile
gender = forms.CharField(max_length=1, label='gender')
def save(self, user):
user.gender = self.cleaned_data['gender']
user.save()
my signups/views.py:
from django.template import RequestContext
from django.shortcuts import render_to_response
from .forms import SignupForm
def index(request):
form = MySignupForm(request.POST or None)
if form.is_valid:
???
return render_to_response("signups/index.html", locals(),
context_instance=RequestContext(request))
My index.html is very basic, I just wanted to see the representation of the form:
{% extends 'account/base.html' %}
{% block head_title %}ProjectName{% endblock %}
{% block content %}
<form method="POST" action="">
{{ form.as_p }}
<input type="submit">
</form>
{% endblock %}
You are instantiating the SignupForm, which is the standard form but not your MySignupForm in the view. Change it like this:
def index(request):
form = MySignupForm()
return render_to_response("signups/index.html", locals(),
context_instance=RequestContext(request))