I built an application that uses REST apis to inject information into a huge already existing database for a company. The application is a web form that the user fills out. My application then serializes the user's responses into a json that it uses to send post requests to the existent db.
My Django app also is connected to a SQL Server db where it is saving the responses of the user into the fields that I created in my models.py.
Is there a better way to do this? It seems like I'm saving all the information twice! A waste of space.
I don't think you need anything in your models.py for this particular application. Personally I like the approach of letting the Form handle the saving process. You could do something like this:
import json
from django import forms
class MyForm(forms.Form):
field1 = forms.CharField()
field2 = forms.IntegerField()
...
def save(self):
json_data = json.dumps(self.cleaned_data)
# insert code here to send the data to your REST API
Your view can then simply call the form.save() method in your view, even though this form is not a ModelForm.
if request.POST:
form = MyForm(request.POST)
if form.is_valid():
form.save()
return redirect(success_url)
return render(request, 'form.html', {'form': form})
Related
I didn't understand the way we should write tests for Django views. Because, my case felt different than others.
I have a view, let's say MyView, inherits from CreateView of Django generic class based views. But, in the background, I mean in the form_valid function, this view tries to connect to a remote server with a Python library, then fetches data according the input of the user, then process other details and saves the object if everything is OK, if not, it returns custom errors.
What should be my way to handle this kind of view so I can write a test?
(I can use something like that but it's not good to handle possible errors:
from django.test import Client
c = Client()
c.login(mydetails..)
y = c.post('/url/', {'my': 'data'})
It works, yes, but I only check the status code in this case. y.status_code can't help, because Django returns 200 even if there is an error in the form.
To simplify testing framework in django, the test will simulate client request (like a user clicked on a link "url") this url is associated with a view which will handle the request and return a response to the user. for example in your test you will assert if the view renders the correct response and template. let suppose that we have a view for creating a blog, how might be tested? see the code below.
views.py
def createblog_view(request):
if request.method == 'POST':
form = BlogForm(request.POST)
if form.is_valid():
blog = form.save(commit=False)
blog.author = request.user # save the user who created the blog
blog.save()
return redirect(blog.get_absolute_url())
else:
form = BlogForm()
context = {'form': form}
return render(request, 'blog/createblog.html', context)
test.py
class BlogTest(TestCase):
def setUp(self):
# this user will be used to create the blog
self.user = User.objects.create_superuser(
'foo',
'foo#test.com',
'password'
)
def test_create_blog(self): # create update and delete a blog
# log user in and user
self.client.login(username='foo', password='password')
# create new blog
# expected date from the user, you can put invalid data to test from validation
form_data = {
'title': 'new test blog',
'body': 'blog body'
}
form = BlogForm(data=blogform_data) # create form indstance
"""
simulate post request with self.client.post
/blog/createblog/ is the url associated with create_blog view
"""
response = self.client.post('/blog/createblog/', form_data)
# get number of created blog to be tested later
num_of_blogs = Blog.objects.all().count()
# get created blog
blog = Blog.objects.get(title=form_data['title'])
# test form validation
self.assertTrue(blogform.is_valid())
# test slugify method, if any
self.assertEqual(blog.slug, 'new-test-blog')
# test if the blog auther is the same logged in user
self.assertEqual(blog.author, self.user1)
# one blog created, test if this is true
self.assertEqual(num_of_blogs, 1)
# test redirection after blog created
self.assertRedirects(
createblog_response,
'/blog/new-test-blog/',
status_code=302,
target_status_code=200
)
I am trying to implement a form wizard at the registration/signup process. I am using django-allauth for authentication and based on the docs and a previous question How to customize user profile when using django-allauth It describes how to add extra fields to the sign up form. I don't really see how I can override the default form to use a form wizard. One option I was considering is adding all the extra fields to the signup form then displaying section of the forms with ajax but I am not sure how to implement validation on the different sections. Any guidance or help on how to implement the registration step as a wizard would be greatly appreciated.
I recently did this by:
views.py
from allauth.account.forms import SignupForm
from allauth.account.utils import complete_signup
SIGNUP_FORMS = [('signup', SignupForm),
('profile', forms.UserProfileForm)]
TEMPLATES = {'signup': 'trips/forms/wizard_form.html',
'profile': 'trips/forms/wizard_form.html'}
class SignupWizard(SessionWizardView):
def get_template_names(self):
return [TEMPLATES[self.steps.current]]
def done(self, form_list, **kwargs):
for form in form_list:
if isinstance(form, SignupForm):
user = form.save(self.request)
complete_signup(self.request, user, settings.ACCOUNT_EMAIL_VERIFICATION, settings.LOGIN_REDIRECT_URL)
elif isinstance(form, forms.UserProfileForm):
userprofile = form.save(commit=False)
user = self.request.user
userprofile.user = user
userprofile.save()
return HttpResponseRedirect(settings.LOGIN_REDIRECT_URL)
You can add as many forms as you want. In my case, the UserProfileForm is a ModelForm that creates a new UserProfile object with a one-to-one relationship to the User. Both objects are only saved after both forms are submitted successfully. The complete_signup function is from allauth and it does some cleanup and then logs the user in to the site.
I ended up implementing the Wizard from the client side using AngularJS Django-angular package and this library. After digging through the allauth signup view code, I figured out it already implemented an AjaxCapableProcessFormViewMixin
Implementing a wizard using client side code for the sign up process when using django-allauth is probably the best way to go since you can delay the successful redirection till all forms in the wizard are filled and also prevents splitting long signup forms into smaller forms.
I have just started learning django. I created a form from django models. On the click of submit button the data is getting stored in database. Now what i want is something like the one given below :
#view.py
def contact(request):
if request.method == 'POST':
form = UserForm(request.POST)
if form.is_valid():
user = form.save()
return HttpResponseRedirect("/contact/create_db")
#urls.py
(r'^contact/$', views.contact),
(r'^contact/create_db$', views.do_create),
Now when i define do_create function in views.py i want to pass the arguments(user data of user form) like this:
def do_create(request, password, dbname, admin_password, confirm_password, demo_data=False, language=None, **kw):
Is this possible using django. How can this be achieved.
All you're asking here is how to get the value of the saved user in a subsequent view.
Well, this is easy. Once the user is saved, it (like any model instance) gets a pk value. You can use this in the URL for the subsequent view.
url(r'^contact/create_db/(?P<user_id>\d+)/$', views.do_create, 'do_create'),
In contact:
from django.shortcuts import redirect
...
user = form.save()
return redirect('do_create', kwargs={'user_id': user.pk})
And in do_create:
def do_create(request, user_id):
user = User.objects.get(pk=user_id)
Note the way I've passed in the URL name and arguments into redirect, rather than hard-coding the URL.
You don't need to pass them into a view. You want to pass them into a form. And you are already doing this:
form = UserForm(request.POST)
You pass your POST data into the form. If you want to do something with this data, use form.cleaned_data dictionary after form validation. Reading docs is also a good idea.
I am newbie in Django and wanted to know a very basic thing:
I have a form which has some dropdown boxes and textfields and upon hitting the button on the form, I want the data to be inserted into database table.
I have already created the table using model.py but dont know where the insert code would come.
The usual way to go about is to create a ModelForm if the data you are collecting is mapping to a model. You place the ModelForm in forms.py inside your app.
# forms.py
from django import forms
from someapp.models import SomeModel
class SomeForm(forms.ModelForm):
class Meta:
model=SomeModel
# views.py
from someapp.forms import SomeForm
def create_foo(request):
if request.method == 'POST':
form = SomeForm(request.POST)
if form.is_valid():
# form.save() saves the model to the database
# form.save does only work on modelforms, not on regular forms
form.save()
..... return some http response and stuff here ....
Read more here:
Forms and ModelForms.
I want to make a form that once submitted, on each next rendering of the form for the same user, the form will be already filled out with the data from the last time the user submitted it. sounds like something that django should have shortcut for - does it have it? if not what would be the easiest way to do it?
The best I could come up with is pickeling cleaned_data and saving it as a cookie, recreating the form from it later.
This already sounds like a good plan. But if there is no pressure on using cookies, I would resort to using the session instead. That way you won't hit a size limit on cookies, and transmitting cookies with higher size can slow down your response time.
form = MyForm(initial=request.session.get('form_data'))
...
if form.is_valid():
request.session['form_data'] = form.cleaned_data
As an extension to this, here is a way I have found to use sessions to save the form data with a class-based view:
from django.views.generic.edit import FormView
class MyFormView(FormView):
template_name = 'myform.html'
form_class = MyForm
success_url = '/success/'
def get_form_kwargs(self):
"""
The FormMixin uses this to populate the data from the POST request.
Here, first try to populate it from the session data, if any;
if there is POST data, it should override the session data.
"""
kwargs = {'data': self.request.session.get('form_data', None)}
kwargs.update(super(MyFormView, self).get_form_kwargs())
return kwargs
def form_valid(self, form):
...
# save the form data to the session so it comes up as default next time
self.request.session['form_data'] = form.cleaned_data
...