Mocking models used in a Django view with arguments - python

For the life of me I cannot figure this out and I'm having trouble finding information on it.
I have a Django view which accepts an argument which is a primary key (e.g: URL/problem/12) and loads a page with information from the argument's model.
I want to mock models used by my view for testing but I cannot figure it out, this is what I've tried:
#patch('apps.problem.models.Problem',)
def test_search_response(self, problem, chgbk, dispute):
problem(problem_id=854, vendor_num=100, chgbk=122)
request = self.factory.get(reverse('dispute_landing:search'))
request.user = self.user
request.usertype = self.usertype
response = search(request, problem_num=12)
self.assertTemplateUsed('individual_chargeback_view.html')
However - I can never get the test to actually find the problem number, it's as if the model does not exist.

I think that's because if you mock the entire model itself, the model won't exist because any of the functions to create/save it will have been mocked. If Problem is just a mock model class that hasn't been modified in any way, it knows nothing about interacting with the database, the ORM, or anything that could be discoverable from within your search() method.
One approach you could take rather than mocking models themselves would be to create FactoryBoy model factories. Since the test database is destroyed with each test run, these factories are a great way to create test data:
http://factoryboy.readthedocs.io/en/latest/
You could spin up a ProblemFactory like so:
class ProblemFactory(factory.Factory):
class Meta:
model = Problem
problem_id = factory.Faker("pyint")
vendor_num = factory.Faker("pyint")
chgbk = factory.Faker("pyint")
Then use it to create a model that actually exists in your database:
def test_search_response(self, problem, chgbk, dispute):
problem = ProblemFactory(problem_id=854, vendor_num=100, chgbk=122)
request = self.factory.get(reverse('dispute_landing:search', kwargs={'problem_id':problem.id}))
request.user = self.user
request.usertype = self.usertype
response = search(request, problem_num=854)
self.assertTemplateUsed('individual_chargeback_view.html')

Related

How to have 2 Types with the same model in Graphene?

I created a model Checkout on my project, with a CheckoutType to handle the requests, but now i need a Profile, that is basically just getting many of the fields on Checkout. The problem is that Checkout and Profile will be retrieved by users with very different permissions, and the while the first one will have the right ones, the second one must not have them. so i went with creating 2 types:
Checkout:
class CheckoutType(ModelType):
class Meta:
model = Checkout
interfaces = [graphene.relay.Node]
connection_class = CountableConnection
permissions = ['app.view_checkout']
filter_fields = {
'zone': ['exact'],
'vehicle__mark': ['exact'],
'status': ['exact']
}
Profile:
class ProfileFilter(django_filters.FilterSet):
class Meta:
model = Checkout
fields = ['zone','status']
#property
def qs(self):
# The query context can be found in self.request.
return super(ProfileFilter, self).qs.filter(salesman=self.request.user)
class ProfileType(ModelType):
class Meta:
model = Checkout
interfaces = [graphene.relay.Node]
connection_class = CountableConnection
filterset_class = ProfileFilter
The thing here is that, the first one shouldn't filter, and just be a regular schema, while the second one should filter by the user that made the request, that and the permissions is the reason i use 2, but as soon as i implemented, all the tests i did for the Checkout Type started to fail, since it seems it tries to use the ProfileType. I searched a little, and it seems that relay only allows a type per model in Django, so this approach doesn't seems possible, but i'm not sure how to overwrite the CheckoutType on another schema, or how to make a second Type with different permissions and different filters. Does someone knows if this is possible?
Just in case someone is on the same boat, i think i found a way to make it work, but with a different approach, i just modified the CheckoutType a little:
class CheckoutType(ModelType):
# Meta
#classmethod
def get_queryset(cls, queryset, info):
if info.context.user.has_perm('app.view_checkout'):
return queryset
return queryset.filter(salesman=info.context.user)
Basically here i remove the permission from the Meta, since i don't want to check that there, and then i overwrite the get_queryset() to check if the user has the perms, if that's the case, then just return the normal query, but if not just filter(And any additional thing you want to do for people without the permission). I'm not sure if there's a better way, but definitely did the job.

Flask Sqlalchemy add multiple row

I am using flask-restful this is
My class I want to insert
class OrderHistoryResource(Resource):
model = OrderHistoryModel
schema = OrderHistorySchema
order = OrderModel
product = ProductModel
def post(self):
value = req.get_json()
data = cls.schema(many=True).load(value)
data.insert()
In my model
def insert(self):
db.session.add(self)
db.session.commit()
schema
from config.ma import ma
from model.orderhistory import OrderHistoryModel
class OrderHistorySchema(ma.ModelSchema):
class Meta:
model = OrderHistoryModel
include_fk = True
Example Data I want to insert
[
{
"quantity":99,
"flaskSaleStatus":true,
"orderId":"ORDER_64a79028d1704406b6bb83b84ad8c02a_1568776516",
"proId":"PROD_9_1568779885_64a79028d1704406b6bb83b84ad8c02a"
},
{
"quantity":89,
"flaskSaleStatus":true,
"orderId":"ORDER_64a79028d1704406b6bb83b84ad8c02a_1568776516",
"proId":"PROD_9_1568779885_64a79028d1704406b6bb83b84ad8c02a"
}
]
this is what i got after insert method has started
TypeError: insert() takes exactly 2 arguments (0 given)
or there is another way to do this action?
Edited - released marshmallow-sqlalchemy loads directly to instance
You need to loop through the OrderModel instances in your list.
You can then use add_all to add the OrderModel objects to the session, then bulk update - see the docs
Should be something like:
db.session.add_all(data)
db.session.commit()
See this post for brief discussion on why add_all is best when you have complex ORM relationships.
Also - not sure you need to have all your models/schemas as class variables, it's fine to have them imported (or just present in the same file, as long as they're declared before the resource class).
You are calling insert on list cause data is list of model OrderHistoryModel instances.
Also post method doesn't need to be classmethod and you probably had an error there as well.
Since data is list of model instances you can use db.session.add_all method to add them to session in bulk.
def post(self):
value = req.get_json()
data = self.schema(many=True).load(value)
db.session.add_all(data)
db.session.commit()

How do I test a Django CreateView?

I want to practice testing on Django, and I have a CreateView I want to test. The view allows me to create a new post and I want to check if it can find posts without a publication date, but first I'm testing posts with published date just to get used to syntax. This is what I have:
import datetime
from django.test import TestCase
from django.utils import timezone
from django.urls import reverse
from .models import Post, Comment
# Create your tests here.
class PostListViewTest(TestCase):
def test_published_post(self):
post = self.client.post('/post/compose/', {'author':"manualvarado22", 'title': "Super Important Test", 'content':"This is really important.", 'published_date':timezone.now()})
response = self.client.get(reverse('blog:post_detail'))
self.assertContains(response, "really important")
But I get this:
django.urls.exceptions.NoReverseMatch: Reverse for 'post_detail' with no
arguments not found. 1 pattern(s) tried: ['post/(?P<pk>\\d+)/$']
How do I get the pk for that newly created post?
Thank you!
You can get it directly from the database.
Note, you shouldn't call two views in your test. Each test should only call the code it is actually testing, so this should be two separate views: one to call the create view and assert that the entry is in the db, and one that creates an entry directly and then calls the detail view to check that it displays. So:
def test_published_post(self):
self.client.post('/post/compose/', {'author':"manualvarado22", 'title': "Super Important Test", 'content':"This is really important.", 'published_date':timezone.now()})
self.assertEqual(Post.objects.last().title, "Super Important Test")
def test_display_post(self):
post = Post.objects.create(...whatever...)
response = self.client.get(reverse('blog:post_detail', pk=post.pk))
self.assertContains(response, "really important")
What you wanna do with testing is using the relyable Django Databse API for recevieng the created data and see if your view represents this data.
As you only create 1 model instance and save it. You may obtain its pk via
model_pk = Post.objects.get(author="manualvarado22").pk
This pk then should be inserted into your url as the Exception states.
But i also recommend abseconded test, where you directly check if the newly created "Post" exists in the DB via django model API.
Edit:
When testing, django or the test Module you are using, creates a clean database only for testing and destroys it after the test-run. So if you want to acces a User while testing you must have created the user in your Test setup or in the Test method itself. Otherwise the Usertable would be empty.
I was finally able to solve the issue thanks to your great answers as well as some extra SO research. This is how the test looks like:
def test_display_no_published_post(self):
test_user = User.objects.create(username="newuser", password="securetestpassword")
post = Post.objects.create(author=test_user, title="Super Important Test", content="This is really important.")
response = self.client.get(reverse('blog:post_detail', kwargs={'pk':post.pk}))
self.assertEqual(response.status_code, 404)
And this are the create and detail views:
class PostDetailView(DetailView):
model = Post
def get_queryset(self):
return Post.objects.filter(published_date__lte=timezone.now())
class PostCreateView(LoginRequiredMixin, CreateView):
login_url = '/login/'
redirect_field_name = 'blog/post_detail.html'
form_class = PostForm
model = Post

How can I test post-save, pre-save in the full system testing in Django with REST

I have the application in Django REST as backend and Angular as frontend.
Suppose in This is my code
class ModelClass (models.Model):
name = models.CharField(max_length=100)
email = models.EmailField()
def save(self, *args, **kwargs):
#check if the row with this hash already exists.
if not self.pk:
self.hash = self.create_hash()
self.my_stuff = 'something I want to save in that field'
# call to some async task
super(ModelClass, self).save(*args, **kwargs)
In my REST i have this view
class ModelListCreateView(generics.ListCreateAPIView):
model = ModelClass
serializer_class = ModelClassSerializer
def pre_save(self, obj):
obj.created_by = obj.updated_by = self.request.user.staff
def post_save(self, obj, created=False):
# add some other child objects of other model
I don't want to do unit testing. I want to do system testing so that I need to know if I post something to that view then
Pre-save thing should work
Record gets created
Save method of Model gets called with his stuff
After save method in REST gets called
Then I can assert all that stuff.
Can I test all that . I want to know which thing I need to have that sort of test rather than small unit tests
I am confused do I need to use Django test or REST Test or selenium test or PyTEst or factory boy because i want to know if things are actually getting in database
What you are looking is some kind of a REST Client code that would then be able to run your tests and you would be able to verify if the call is successful or not. Django Rest Framework has the APIRestFactory helper class that will aid you in writing such tests. The documentation can be found here and specifically look at the Example section. This would be part of your Django tests

App engine datastore query issue

I have a weired problem with couple of queries I am trying to run.
I have built a method which returns a tuple of result from the query-
def get_activeproducts():
query = Product.gql("WHERE active = True")
choices = []
for obj in query:
choices.append((str(obj.key()), obj.name))
return choices
The problem is, the result is same for each call. Even if products are deleted or changed to 'False' in the product attribute 'active'. The result will be refreshed only when I restart the sdk server. In production, it just doesnt change till I change versions.
I have seen similar issue with one more query where the query property is BooleanProperty.
Any idea on how this could be fixed?
EDIT:
I am using the method in a tipfy application. It is used to populate a select field in wtforms. 'choices' basically takes in a list of tuples (value, name) pair.
class InvoiceForm(Form):
product = SelectField('Product', choices=get_activeproducts())
I dont have any issue with editing. WHen I check it from the admin end, I can see that certain products are set to 'False'. And even if I empty(delete) the whole list of products, I get the same list I got the first time.
I am not using caching anywhere in the application.
Your class definition is getting cached by the App Engine runtime when an instance is started, with the default set to what it was when the instance started. To make the choices dynamic, you need to set them at runtime.
Example from the wtforms (which IIRC is what tipfy is using) docs; will need to be adjusted for App Engine queries:
class UserDetails(Form):
group_id = SelectField(u'Group', coerce=int)
def edit_user(request, id):
user = User.query.get(id)
form = UserDetails(request.POST, obj=user)
form.group_id.choices = [(g.id, g.name) for g in Group.query.order_by('name')]
when you create your form, the function is called once.
you can overload the form __init__.py function to do this cleanly
class InvoiceForm(Form):
product = SelectField(u'Group', choices=[])
def __init__(self, product_select, *args, **kwargs)
super(InvoiceForm, self).__init__(*args, **kwargs)
self.product.choices = select_dict
----
form = InvoiceForm(product_select=get_activeproducts())

Categories

Resources