I want to test some django application urls. However, the associated views are linked to the database. What I'd like to do is mocking these aspects of the view method, but I have no idea how.
Let's suppose I want to try the /signin url, whici is a classical signin form.
The associated view looks like this :
def login(request):
if 'user' in request.session:
return redirect(reverse("home"))
if request.method == 'POST':
form = LoginForm(request.POST)
if form.is_valid():
username = form.cleaned_data['username']
password = form.cleaned_data['password']
return treat_login(request, username, password) # checks if couple
is present in
database, returns
pages accordingly
else:
form = LoginForm()
return render(request, 'login.html', {'form':form, })
In my test, I have no implicit call to the login method, since I only use the url :
class Tests_urls(TestCase):
def test_signin(self):
self.client.post(reverse("login"), {"username":"login", "password":"pwd"})
self.assert_http_status(url, status, "after a standard login")
The problem with that test is that it needs a database to be performed, wich is what I want to avoid (I can't use use the embedded test database).
As a consequence, I would like to know how to mock the treat_login method from the test point of view.
You can use patch from the mock libarary
from mock import patch
class Tests_urls(TestCase):
#patch('my_app.views.treat_login')
def test_signin(self, mock_treat_login):
self.client.post(reverse("login"), {"username":"login", "password":"pwd"})
self.assert_http_status(url, status, "after a standard login")
self.assertTrue(mock_treat_login.called)
You can also inspect the call args. But the way you have written this test makes that a bit hard. If you used the request factory and tested the function by doing something like
request = self.factory.post(
reverse("login"), {"username":"login", "password":"pwd"})
response = login(request
mock_treat_login.assert_called_once_with(request, "login", "pwd)
Then you could actually make sure you were calling it correctly.
Related
We want to access the same variable in every function inside our views.py. Since it is not constant, we cannot use it as a global variable.
Is it possible to pass a variable to another function while also rendering an HTML template? What are the alternatives if none exist?
This is our login function in views.py
def loginpage(request):
errorMessage = ''
# Applicant Login
if request.method=="POST":
if request.POST.get('username') and request.POST.get('pwd'):
try:
currentUser=Applicant.objects.get(username=request.POST['username'],pwd=request.POST['pwd'])
currentUser=Applicant.objects.get(username=request.POST['username'])
first = currentUser.firstname
middle = currentUser.middleinitial
last = currentUser.lastname
AppDashboard = ApplicantDashboardPageView(currentUser, request)
except Applicant.DoesNotExist as e:
errorMessage = 'Invalid username/password!'
return render(request, 'home.html')
The currentUser variable inside our login function is the variable we want to pass in this function
def ApplicantdashboardPageView(currentUser, request):
appPeriod = ApplicationPeriod.objects.all()
exam = ExaminationSchedule.objects.all()
posts = Post.objects.all().order_by('-created_on')
form = PostForm()
name=userNaCurrent
print('from storeCurrentUser', name)
if request.method == "GET":
try:
posts = Post.objects.all().order_by('-created_on')
form = PostForm()
#applicantID=currentUser.id
#applicantNotification = Applicant.objects.get(id=applicantID)
return render(request, 'applicantdashboard.html', context={'UserName' : name, 'posts':posts, 'appPeriod':appPeriod, 'exam':exam})
except ObjectDoesNotExist:
return render(request, 'applicantdashboard.html', context={'UserName' : name, 'posts':posts,})
return render(request, 'applicantdashboard.html', context={'UserName' : name, 'posts':posts, 'appPeriod':appPeriod, 'exam':exam})
I am new to Django so please bear with me if my question seem too basic. Thank you
Store raw user password is a very big flaw in security. Please read more about Django Authentication system https://docs.djangoproject.com/en/4.1/topics/auth/
Basically, to store critical confidential information like passwords you need to at least, encrypt it. But for passwords you don't need to see the raw value of it, isn't it? Therefore, you just need to hash it and compare it every time you need to authenticate the user. Read more here Best way to store password in database
Django Auth system will also help to solve the issue by injecting the current user into a "global" request object so that you can access it everywhere.
You can do the same by keeping those 2 methods in a class and accessing variables by creating objects for it.
Hi so I have an attend session button that when clicked adds the user to the session. I got it working but I want to add a check to see whether the user is already in the ManyToMany field of attendees before I add them. How would I go about doing that?
Here is my view for it
def attend_session(request):
session = Study.objects.get(pk=request.POST['session_id'])
stud = Student.objects.get(student_user=request.user)
if request.method == "POST":
# Add check here to see if student is already attending
session.attendees.add(stud)
session.save()
return HttpResponseRedirect(reverse('study:sessions'))
You can check with:
from django.shortcuts import get_object_or_404, redirect
def attend_session(request):
session = get_object_or_404(Study, pk=request.POST['session_id'])
stud = get_object_or_404(Student, student_user=request.user)
if request.method == 'POST':
if stud not in session.attendees.all():
session.attendees.add(stud)
return redirect('study:sessions')
Note: It is often better to use get_object_or_404(…) [Django-doc],
then to use .get(…) [Django-doc] directly. In case the object does not exists,
for example because the user altered the URL themselves, the get_object_or_404(…) will result in returning a HTTP 404 Not Found response, whereas using
.get(…) will result in a HTTP 500 Server Error.
Note: You can make use of redirect(…) [Django-doc] instead
of first calling reverse(…) [Django] and
then wrap it in a HttpResponseRedirect object [Django-doc].
The redirect(…) function does not only offer a more convenient signature to do this, it also for example will use the
.get_absolute_url() method [Django-doc]
if you pass it a model object.
I have simple view in django app, which I want to show only when one of the forms is valid. I have something like:
#login_required
#require_role('admin')
def new_package(request):
invoicing_data_form = InvoicingDataForm(instance=request.user.account.company.invoicingdata)
if invoicing_data_form.is_valid():
# all here
return HttpResponse('Form valid')
else:
logger.info("Form invalid")
return HttpResponse(json.dumps(invoicing_data_form.errors)
I always get log info message that form is invalid, however, I get nothing in
invoicing_data_form.errors
It is very strange, because I am validating this form in other view using user input data and it works just fine. Any idea?
EDIT:
Just for clarification.
I am not requesting any data from user in this form.
I am using this form to validate some model instance (this form is subclassing from ModelForm).
That's because you're not "feeding" your form.
Do this:
invoicing_data_form = InvoicingDataForm(instance=invoice, data=request.POST or None)
You have an unbound form.
https://docs.djangoproject.com/en/1.7/ref/forms/api/#bound-and-unbound-forms
A Form instance is either bound to a set of data, or unbound.
If it’s bound to a set of data, it’s capable of validating that data and rendering the form as HTML with the data displayed in the HTML.
If it’s unbound, it cannot do validation (because there’s no data to validate!), but it can still render the blank form as HTML.
To bind data to a form, pass the data as a dictionary as the first parameter to your Form class constructor:
invoicing_data_form = InvoicingDataForm(request.POST or None, instance=invoice)
If you're already giving request.POST to your form using request.POST or None, but it's still invalid without errors, check that there isn't any redirect going on. A redirect loses your POST data and your form will be invalid with no errors because it's unbound.
I got this for AuthenticationForm which needs AuthenticationForm(None, request.POST) see Using AuthenticationForm in Django
I want to expand on the answer by #yuji-tomita-tomita
I typically use a CBV approach in Django, and how I'm handling forms:
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid():
# do things
Reading the source code I noticed that self.get_form() using get_form_kwargs(self) to populate the form with request.POST, thus getting bound to data. So if you're overloading it like I did:
def get_form_kwargs(self):
company = self.get_company()
return {"company": company}
Make sure to call the super(), and it will finally work:
def get_form_kwargs(self):
company = self.get_company()
kwargs = super().get_form_kwargs()
kwargs.update({"company": company})
return kwargs
I have little experience with this kind of job, I can't find an app that suits my needs well. So I have to write my own code. But I don't know whether there is a better way, this is why I am here.
I know this app django-simple-captcha, it is good but not enough because I want to do a more reasonable job, that is, I don't want bother normal user to fill captcha fields. What I want is: if a user send post requests more than ALLOW_TIMES within the CHECK_SECONDS I defined, the request.is_attack will be True, and I will know to render a django-simple-captcha form instead of the normal form.
I tried this method, using a custom Middleware: I record a user's each post request infomation in a global dict to decide the value of request.is_attack.
import datetime
datetime_now = datetime.datetime.now
USER_POST_INFO={}
CHECK_SECONDS=60
ALLOW_TIMES=3
class AntiAttackMiddleware(object):
def process_request(self, request):
'''
if this user send more than ALLOW_TIMES request
in CHECK_SECONDS, set request.is_attack to True,
otherwise False
'''
user = request.user
if request.method == "POST" and user.is_authenticated():
now = datetime_now()
if user.pk not in USER_POST_INFO:
USER_POST_INFO[user.pk]={
'time':now,
'total':1,
'attack':False,
}
request.is_attack=False
else:
d = USER_POST_INFO[user.pk]
if d['time'] + datetime.timedelta(seconds=CHECK_SECONDS) > now :
d['total'] +=1
if d['total']>ALLOW_TIMES:
d['attack']=True
else:
d['time']=now
d['attack']=False
request.is_attack=d['attack']
In views.py
def formview(request):
if request.is_attack:
#render captcha form
else:
#render normal form
Currently I have a form that was built using the form wizard, the form is processed using a separate script that I wrote. I need to pass the current user (user currently logged in) to this script so that I can run an insert query to my database. Any suggestions on how to do this?
class QuestionWizard(SessionWizardView):
def done(self, form_list, **kwargs):
import process_form
userID = request.user.id
result = process_form.main(form_list,userID)
return render_to_response('done.html', {
#'form_data': [form.cleaned_data for form in form_list],
'data_return': result[0],
})
I believe the form wizard done function only accepts two variables.
It turns out it was a lot simpler than I anticipated. Inside of the done function I added the following:
user_id = self.request.user.id
And then passed the user_id into the process_form function.
Annoying that I overlooked it, but glad that I figured it out.