Python Unittest: combine two tests into one subTest - python

i have two similar tests:
def test_group_posts_context(self):
response = self.author_client.get(
reverse('posts:group_posts', kwargs={'slug': self.group.slug})
)
self.assertEqual(response.context.get('group'), self.group)
def test_author_profile_context(self):
response = self.author_client.get(
reverse('posts:profile', kwargs={'username': self.user.username})
)
self.assertEqual(response.context.get('author'), self.user)
Both tests work fine but I am trying to merge them in one single subTest.
But construction, like this raises a ValueError: too many values to unpack (expected 2).
def test_both_profile_and_group_show_correct_context(self):
templates_page_names = {
reverse('posts:index'),
reverse('posts:group_posts', kwargs={'slug': self.group.slug}),
reverse('posts:profile', kwargs={'username': self.post.author}),
}
for reverse_name in templates_page_names:
with self.subTest(reverse_name=reverse_name):
response = self.author_client.get(reverse_name)
context_fields = {
'group': self.group,
'author': self.user,
}
for value, expected in context_fields:
with self.subTest(value=value):
context_field = response.context.get(value)
self.assertEqual(context_field, expected)
My reviewer says, that I should try to use a Tuple like (page_address, context name, expected object), but I actually cant figure out how to use it here :(.
Views.py:
def group_posts(request, slug):
group = get_object_or_404(Group, slug=slug)
page_obj = get_paginate(
request.GET.get('page'),
group.posts.select_related('author')
)
context = {
'group': group,
'page_obj': page_obj,
}
return render(request, 'posts/group_list.html', context)
def profile(request, username):
author = get_object_or_404(User, username=username)
page_obj = get_paginate(
request.GET.get('page'),
author.posts.select_related('group')
)
context = {
'author': author,
'page_obj': page_obj,
}
return render(request, 'posts/profile.html', context)

Related

Paginating the response of a Django POST request

I'm using Django for my work project. What I need to get is all likes put by some user. Likes are put to transactions, so I am getting the list of all transactions in 'items' field, where the user put his like/dislike. There are some optional parameters that can be in the request:
{
"user_id": (usually is taken from session but can be used to get likes of specific user by admins),
"like_kind_id": (1 for like/2 for dislike),
"include_code_and_name": (True/False),
"page_size": (integer number) for pagination
}
With the last parameter, I am facing the issues. The result should look like this:
{
​ "user_id":id,
​ "likes":[
​ {
"like_kind":{
"id":id,
"code":like_code,
"name":like_name,
},
"items":[
{
"transaction_id":transaction_id,
"time_of":date_created,
},...
]
}
]
}
"Likes" is just array of 2 elements (like and dislike).
So, my problem is that I cannot set pagination for "items" array as there can be a lot of transactions. I am getting the page_size from request.data.get("page_size") but cannot apply it so that the results will be paginated. I know that for GET-requests I need simply set pagination_class in views.py. Anyone knows how can I do the similar procedure for POST-request and set the page_size of pagination class to 'page_size' from data.request? =)
This is my views.py:
class LikesUserPaginationAPIView(PageNumberPagination):
page_size = 1
page_size_query_param = 'page_size'
max_page_size = 1
class LikesUserListAPIView(APIView):
"""
All likes of the user
"""
permission_classes = [IsAuthenticated]
authentication_classes = [authentication.SessionAuthentication,
authentication.TokenAuthentication]
pagination_class = LikesUserPaginationAPIView
#classmethod
def post(cls, request, *args, **kwargs):
like_kind = request.data.get('like_kind')
include_code = request.data.get('include_code')
page_size = request.data.get('page_size')
pagination.PageNumberPagination.page_size = page_size
if include_code is None:
include_code = False
else:
if include_code == "False":
include_code = False
elif include_code == "True":
include_code = True
else:
return Response("Should be True или False for include_code",
status=status.HTTP_400_BAD_REQUEST)
if like_kind is None:
like_kind = "all"
else:
try:
likekind = LikeKind.objects.get(id=like_kind)
except LikeKind.DoesNotExist:
return Response("ID doesn't exists ",
status=status.HTTP_404_NOT_FOUND)
context = {"include_code": include_code, "like_kind": like_kind}
user_id = request.user.id # get the user_id from session
# TODO
# optionally other users with higher privilege can get access to other users' likes
# need to check for the privilege (admin, coordinator, ..)
# user_id = request.data.get('user_id')
if user_id is not None:
try:
user = User.objects.get(id=user_id)
users = [user, user, user, user, user]
serializer = LikeUserSerializer(users, many=True, context=context)
return Response(serializer.data)
except User.DoesNotExist:
return Response("ID doesn't exists ",
status=status.HTTP_404_NOT_FOUND)
return Response("user_id is not given",
status=status.HTTP_400_BAD_REQUEST)
My serializer.py:
class LikeUserSerializer(serializers.ModelSerializer):
likes = serializers.SerializerMethodField()
user_id = serializers.SerializerMethodField()
def get_user_id(self, obj):
return obj.id
def get_likes(self, obj):
include_code = self.context.get('include_code')
like_kind_id = self.context.get('like_kind')
likes = []
if like_kind_id == "all":
like_kinds = [(like_kind.id, like_kind.name, like_kind.get_icon_url()) for like_kind in LikeKind.objects.all()]
else:
like_kinds = [(like_kind.id, like_kind.name, like_kind.get_icon_url()) for like_kind in
[LikeKind.objects.get(id=like_kind_id)]]
# {"user_id": 1}
if include_code:
for like_kind in like_kinds:
items = []
transactions_liked = [(like.transaction_id, like.date_created)
for like in Like.objects.filter_by_user_and_like_kind(obj.id, like_kind[0])]
for transaction1 in transactions_liked:
items.append(
{
"transaction_id": transaction1[0],
"time_of": transaction1[1],
}
)
likes.append(
{
"like_kind": {
'id': like_kind[0],
'name': like_kind[1],
'icon': like_kind[2],
},
"items": items
}
)
return likes
else:
for like_kind in like_kinds:
items = []
transactions_liked = [(like.transaction_id, like.date_created)
for like in Like.objects.filter_by_user_and_like_kind(obj.id, like_kind[0])]
for transaction1 in transactions_liked:
items.append(
{
"transaction_id": transaction1[0],
"time_of": transaction1[1],
}
)
likes.append(
{
"like_kind": {
'id': like_kind[0],
},
"items": items
}
)
return likes
class Meta:
model = Like
fields = ['user_id', 'likes']
In urls.py I have the following path:
path('get-likes-by-user/', likes_views.LikesUserListAPIView.as_view()),
If you have any questions, please ask. Thanks, all!! I will appreciate your help!

returning more than one variable in render of django

I define home request in views.py,
db=client.inventory_data
def home(request):
collection_data_1 = db['orders']
mydata = list(collection_data.find())
return render(request,'home.html',{'mydata': mydata})
The above function works fine but when I try to return one more list, it does not work.
def home(request):
collection_data_1 = db['orders']
collection_data_2 = db['product']
mydata = list(collection_data_1.find())
product_data = list(collection_data_2.find())
return render(request,'home.html',{'mydata': mydata},{'product_data':product_data})
I want to return both the list, how can we achieve this? looking for kind help.
You can simply combine the two dictionaries into one: {'mydata': mydata, 'product_data': product_data}
def home(request):
collection_data_1 = db['orders']
collection_data_2 = db['product']
mydata = list(collection_data_1.find())
product_data = list(collection_data_2.find())
return render(request,'home.html',{'mydata': mydata, 'product_data': product_data})
The reason that it didn't work when you passed in the two dictionaries separately is because render accepts context (a dictionary) as the third argument and content_type (a string) as the fourth argument, so when you passed in two dictionaries, you were passing in a dictionary as the content_type.
If it helps, here's what you originally had with the variable names annotated:
render(
request=request,
template_name='home.html',
context={'mydata':mydata},
content_type={'product_data':product_data},
)
And here's what you have now:
render(
request=request,
template_name='home.html',
context={'mydata': mydata, 'product_data': product_data}
)
def home(request):
# ...
return render(request,'home.html',{'mydata': mydata, 'product_data':product_data})
def home(request):
collection_data_1 = db['orders']
collection_data_2 = db['product']
mydata = list(collection_data_1.find())
product_data = list(collection_data_2.find())
context = {
'mydata': mydata,
'product_data': product_data
}
return render(request,'home.html', context=context)

AttributeError at /update_item/ 'WSGIRequest' object has no attribute 'data'

i was trying to make my first e-commerce website using django and i received this error.
I already search in google but I can't find how to fix it. please help me to find the error.
this is attached. If any data is missing please comment.
cart.js :
var updateBtns = document.getElementsByClassName('update-cart')
for (var i = 0; i < updateBtns.length; i++) {
updateBtns[i].addEventListener('click', function () {
var productId = this.dataset.product`enter code here`
var action = this.dataset.action
console.log('productId:', productId, 'Action:', action)
console.log('USER:', user)
if (user == 'AnonymousUser') {
console.log('Not logged in')
} else {
updateUserOrder(productId)
}
})
}
function updateUserOrder(productId, action) {
console.log('User is logged in, sending data...')
var url = '/update_item/'
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrftoken,
},
body: JSON.stringify({ 'productId': productId, 'action': action })
})
.then(response => response.json())
.then((data) => {
console.log('data:', data);
}
);
}
views.py :
from django.shortcuts import render
from django.http import JsonResponse
from django.http import HttpRequest
import json
from .models import *
def store(request):
products = Product.objects.all()
context = {'products': products}
return render(request, 'store/store.html', context)
def cart(request):
if request.user.is_authenticated:
customer = request.user.customer
order, created = Order.objects.get_or_create(
customer=customer, complete=False)
items = order.orderitem_set.all()
else:
# Create empty cart for now for non-logged in user
items = []
order = {'get_cart_total': 0, 'get_cart_items': 0}
context = {'items': items, 'order': order}
return render(request, 'store/cart.html', context)
def checkout(request):
if request.user.is_authenticated:
customer = request.user.customer
order, created = Order.objects.get_or_create(
customer=customer, complete=False)
items = order.orderitem_set.all()
else:
# Create empty cart for now for non-logged in user
items = []
order = {'get_cart_total': 0, 'get_cart_items': 0}
context = {'items': items, 'order': order}
return render(request, 'store/checkout.html', context)
def updateItem(request):
data = json.loads(request.data)
productId = data['productId']
action = data['action']
print('Action:', action)
print('productId:', productId)
return JsonResponse('Item was added', safe=False)'''
def updateItem(request):
data = json.loads(request.data)
request.data is available in Django rest framework, but it doesn't exist in regular Django views.
Change the view to use request.body instead.
def updateItem(request):
data = json.loads(request.body)

Django : view calls an other template (Pagination )

I have this view:
class DamageListCriteria(TemplateView):
template_name = "damage/damagelist_criteria.html"
def get(self, request):
form = DamageListCriteriaForm()
general = General.objects.get(pk=1)
args = {
'form': form,
'general': general
}
return render(request, self.template_name, args)
def post(self, request):
general = General.objects.get(pk=1)
form = DamageListCriteriaForm(request.POST)
form.non_field_errors()
if form.is_valid():
fromdate = request.POST.get('fromdate')
fdate = datetime.strptime(fromdate, '%d/%m/%Y')
fdate = datetime.combine(fdate, datetime.min.time(), tzinfo=pytz.UTC)
print('fdate ', fdate)
todate = form.cleaned_data['todate']
#tdate = datetime.strptime(todate, '%d/%m/%Y') + timedelta(days=1)
tdate = datetime.strptime(todate, '%d/%m/%Y')
tdate = datetime.combine(tdate, datetime.max.time(), tzinfo=pytz.UTC)
print('tdate ', tdate)
d_list = Damage.objects.filter(entry_date__range=(fdate, tdate))
paginator = Paginator(d_list, 1)
page = request.GET.get('page')
try:
damage_list = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer, deliver first page.
damage_list = paginator.page(1)
except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results.
damage_list = paginator.page(paginator.num_pages)
template = "damage/damagelist_table.html"
form = DamageListForm()
general = General.objects.get(pk=1)
fromdatetext = fdate.strftime('%d/%m/%Y')
todatetext = tdate.strftime('%d/%m/%Y')
args = {
'form': form,
'damage_list': damage_list,
'general': general,
'fromdate': fromdatetext,
'todate': todatetext
}
return render(request, template, args)
else:
print('form is not valid')
print(form.errors)
# form = DamageEntryForm()
args = {'form': form,
'general': general
}
return render(request, self.template_name, args)
I want to get some criteria to make a filtering listing of my database.
It worked this way , until the moment i tried to add pagination.
The url is http://127.0.0.1:8000/damage/damage/list/criteria/
url(r'damage/list/criteria/$', views.DamageListCriteria.as_view(), name="damage-list-criteria"),
Next and Previous don't work because I am still at this url after the
return render(request, template, args)
Can i redirect somehow?
I understand that this might be the wrong way to do the listing. Can you help me , how to do it?
Thanks a lot
Kostas
The easiest thing to do would be not use Django itself but use Django REST framework and reuse its serializer classes along with APIView (or one of its subclasses). Are you in a position to use it or are you constrained?

Update the Queryset of a Django-Select2 AutoModelSelect2Field

I can't figure out how to update the queryset of a AutoModelSelect2Field dynamically. I'm getting really strange results. For example, sometimes the select2 box will return the correct, filtered results, and sometimes it will return NO results when I enter the same characters.
my code:
#views.py
form = MyForm()
#forms.py
class MyField(AutoModelSelect2Field):
search_fields = ['name__icontains']
max_results = 10
class MyForm(forms.Form):
my_field = MyField(
queryset=project.objects.none(),
required=True,
widget=AutoHeavySelect2Widget(
select2_options={
'width': '100%',
}
)
)
def __init__(self, *args, **kwargs):
qs = kwargs.pop('queryset')
self.base_fields['my_field'].queryset = qs
super(MyForm, self).__init__(*args, **kwargs)
#self.fields['my_field'].queryset = qs
#self.base_fields['my_field'].queryset = qs
A few of the things I've tried -
update from the view:
#views.py
form = MyForm()
form.base_fields['my_field'].queryset = new_qs
and:
form = MyForm()
form.fields['my_field'].queryset = new_qs
pass the qs to the form:
#views.py
form = MyForm(queryset=Project.objects.filter(project_type=pt))
# see above code for forms.py
I've also tried setting the initial qs to all objects:
class MyForm(forms.Form):
my_field = MyField(
queryset=project.objects,
...
But I get the same problem, 90% of the time I get the results of the initial queryset, rather than the filtered objects based on the new qs.
We were able to find a pretty straightforward way to get the dropdown options to filter by additional fields (ie. first select country and then have the state dropdown only showing states from the selected country)
It was inspired by a suggestion from here (where we also posted this solution):
https://github.com/applegrew/django-select2/issues/22
in forms.py:
class StateChoices(AutoModelSelect2Field):
queryset = State.objects
def get_results(self, request, term, page, context):
country = request.GET.get('country', '')
states = State.objects.filter(country=country, name__istartswith=term)
s2_results = [(s.id, s.name, {}) for s in states]
return ('nil', False, s2_results)
the form field:
# only include this when you would have a screen where the country
# is preset and would not change, but you want to use it as a search context
country = forms.ModelChoiceField(queryset=Country.objects.all(),
widget=forms.HiddenInput())
state = StateChoices(widget = AutoHeavySelect2Widget(
select2_options = {
'minimumInputLength': 1,
'ajax': {
'dataType': 'json',
'quietMillis': 100,
'data': JSFunctionInContext('s2_state_param_gen'),
'results': JSFunctionInContext('django_select2.process_results'),
}
}))
in our javascript:
function s2_state_param_gen(term, page) {
var proxFunc = $.proxy(django_select2.get_url_params, this);
results = proxFunc(term, page, 's2_condition_param_gen');
results.country = $('#id_country').val();
return results;
}

Categories

Resources