I have been pulling my hair due to this for quite some time. ModelField seems like a good option in my case, but I can't seem to get it to work. I am working on a search-engine for a project.
serializers.py
class FullSearchSerializer(serializers.Serializer):
profiles = serializers.ModelField(model_field=Profiles())
cv = serializers.ModelField(model_field=CV())
I get this little fantastic error:
'Profiles' object has no attribute '_get_val_from_obj'
The reason I'm doing this is because I fetch, lists and paginates several models before I serialize it. As such: (views.py)
result_list = []
for target in targets:
result = targets[target]['model'].objects.select_related().filter(reduce(operator.or_, targets[target]['query_list']) & targets[target]['extra_query'])
if result:
result_list.append(result)
print result_list
results = list(
sorted(
chain(*result_list),
key = lambda objects: objects.id,
)
)
print results
page = request.QUERY_PARAMS.get('page')
paginated_result = paginate(results, page)
serializer_context = {'request': request}
serialized = PaginatedSearchSerializer(paginated_result, context=serializer_context)
print serialized.data
return JSONResponse(serialized.data, status=status.HTTP_200_OK)
return JSONResponse({'message': 'No results'})
"targets[target]['model']" and so on comes from a dictionary which i loops though, getting the right model, serializers and so on. Since I am making a function where you are able to search though all, without specifying targets and I need a serializer that can handle that.
The complete search-class:
class Search(APIView):
#method_decorator(req_login)
#transaction.non_atomic_requests
def get(self, request, *args, **kwargs):
""" Defining variables """
count_result = 0
result = None
target = None
serialized = None
search_query = ''
result_list = {}
""" Checks so values are there. """
if 'target' in kwargs:
target = kwargs['target']
if 'q' in request.GET:
search_query = request.GET['q']
""" List of modeltions and methods for search. """
targets = {
'user': {
'model': Profiles,
'query_list': [
Q(profiledata__full_name__icontains=search_query)
],
'extra_query': Q(profiledata__role__role='applicant'),
'serializer': PaginatedProfileSerializer
},
'cv': {
'model': CV,
'query_list': [
Q(skills__icontains=search_query),
Q(education__icontains=search_query),
Q(experience__icontains=search_query),
Q(interests__icontains=search_query),
Q(languages__icontains=search_query),
Q(driverlicense__icontains=search_query)
],
'extra_query': Q(),
'serializer': CVSerializer
},
'corporation': {
'model': Corporation,
'query_list': [
Q(name__icontains=search_query),
Q(description__icontains=search_query),
Q(keywords__title__icontains=search_query)
],
'extra_query': Q(),
'serializer': PaginatedCorporationSerializer
},
'jobs': {
'model': Jobs,
'query_list': [
Q(title__icontains=search_query),
Q(description__icontains=search_query),
Q(keywords__title__icontains=search_query)
],
'extra_query': Q(),
'serializer': PaginatedJobSerializer
}
}
""" Making stuff happen and returns result """
if target:
result = targets[target]['model'].objects.select_related().filter(reduce(operator.or_, targets[target]['query_list']) & targets[target]['extra_query'])
if result:
count_result = result.count()
page = request.QUERY_PARAMS.get('page')
paginated_result = paginate(result, page)
serializer_context = {'request': request}
serialized = targets[target]['serializer'](paginated_result, context=serializer_context, many=True)
return JSONResponse({'query': serialized.data, 'count': count_result}, status=status.HTTP_200_OK)
else:
result_list = []
for target in targets:
result = targets[target]['model'].objects.select_related().filter(reduce(operator.or_, targets[target]['query_list']) & targets[target]['extra_query'])
if result:
result_list.append(result)
print result_list
results = list(
sorted(
chain(*result_list),
key = lambda objects: objects.id,
)
)
print results
page = request.QUERY_PARAMS.get('page')
paginated_result = paginate(results, page)
serializer_context = {'request': request}
serialized = PaginatedSearchSerializer(paginated_result, context=serializer_context)
print serialized.data
return JSONResponse(serialized.data, status=status.HTTP_200_OK)
return JSONResponse({'message': 'No results'})
Related
Am testing an endpoint that retrieves data using a ModelViewSet, and am passing a param via a URL to it to get data but am getting this error when I run the unit tests:
File "/Users/lutaayaidris/Documents/workspace/project_sample/project_sample/financing_settings/tests.py", line 195, in test_get_blocks
self.block_get_data), content_type='application/json')
File "/Users/lutaayaidris/Documents/workspace/project_sample/lib/python3.6/site-packages/rest_framework/test.py", line 286, in get
response = super().get(path, data=data, **extra)
File "/Users/lutaayaidris/Documents/workspace/project_sample/lib/python3.6/site-packages/rest_framework/test.py", line 194, in get
'QUERY_STRING': urlencode(data or {}, doseq=True),
File "/Users/lutaayaidris/Documents/workspace/project_sample/lib/python3.6/site-packages/django/utils/http.py", line 93, in urlencode
for key, value in query:
ValueError: not enough values to unpack (expected 2, got 1)
This is how I have structured my tests , plus some dummy data for testing :
class TemplateData:
"""Template Mock data."""
step_get_data = {
"param": "step"
}
block_get_data = {
"param": "block"
}
get_no_data = {
"param_": "block"
}
class TemplateViewTests(TestCase, TemplateData):
"""Template Tests (Block & Step)."""
def setUp(self):
"""
Initialize client, Step and Block id and data created.
"""
self.client = APIClient()
self.block_id = 0
self.step_id = 0
self.create_block_step_data()
def create_block_step_data(self):
"""Create ProcessVersion, Step, & Block mock data."""
self.process_version = ProcessVersion.objects.create(
tag="TESTING_TAG",
is_process_template=False,
status="IN EDITING",
attr_map="TESTING_ATTR",
loan_options=None
)
self.step = Step.objects.create(
version=self.process_version,
is_process_template=True,
title="TESTING",
help_text="TESTING",
order=1,
slug="slug",
can_be_duplicated=False,
max_duplicated_number=2,
)
self.step_id = self.step.pk
self.block_id = Block.objects.create(
step=self.step,
is_process_template=True,
title="TESTING",
information_text="This is testing "
"information",
order=1,
depending_field="depending_field",
visibility_value="visibility_value",
slug="slug",
can_be_duplicated=False,
max_duplicated_number=2,
).pk
self.process_version_1 = ProcessVersion.objects.create(
tag="TESTING_TAG",
is_process_template=False,
status="IN EDITING",
attr_map="TESTING_ATTR",
loan_options=None
)
self.step_1 = Step.objects.create(
version=self.process_version_1,
is_process_template=True,
title="TESTING",
help_text="TESTING",
order=1,
slug="slug",
can_be_duplicated=False,
max_duplicated_number=2,
)
self.block_1 = Block.objects.create(
step=self.step,
is_process_template=True,
title="TESTING",
information_text="This is testing "
"information",
order=1,
depending_field="depending_field",
visibility_value="visibility_value",
slug="slug",
can_be_duplicated=False,
max_duplicated_number=2,
).pk
def test_get_blocks(self):
"""Test get list of Block. """
response = self.client.get(
"/api/v1/financing-settings/template/",
data=json.dumps(
self.block_get_data), content_type='application/json')
self.assertEqual(response.status_code, 200)
def test_get_steps(self):
"""Test get list of Step. """
response = self.client.get(
"/api/v1/financing-settings/template/",
data=json.dumps(
self.block_get_data),
content_type='application/json')
self.assertEqual(response.status_code, 200)
def test_no_step_or_block(self):
"""Test get no list of Step or Block. """
response = self.client.get(
"/api/v1/financing-settings/template/",
data=json.dumps(
self.block_get_data),
content_type='application/json')
self.assertEqual(response.status_code, 204)
As you can see above those are my tests, I have already setup the data , now I want to retrieve back the data, but because of the exception above I can't.
Lastly, in my endpoint implementation, I used a Viewset to handle this , below is the code :
class TemplateView(ModelViewSet):
"""ViewSet for Saving Block/ Step template."""
def list(self, request, *args, **kwargs):
"""Get list of Block/Steps with is_process_template is equal to True."""
param = request.data['param']
if param == "block":
_block = Block.objects.filter(is_process_template=True).values()
return JsonResponse({"data": list(_block)}, safe=False, status=200)
elif param == "step":
_step = Step.objects.filter(is_process_template=True)
return JsonResponse({"data": list(_step)}, safe=False, status=200)
return Response(status=status.HTTP_204_NO_CONTENT)
What is causing this , in my understanding I feel like everything should work.
The function Client.get expect a dictionary as data argument and try to encode it in the url using the function urlencode. You could do something like that:
from django.test import Client
c = Client()
block_get_data = {
"param": "block"
}
c.get('path', block_get_data)
block_get_data will be sent in the url as 'param=block'
If you want to send JSON formated data in a GET method, you can use Client.generic function as follow:
from django.test import Client
import json
c = Client()
block_get_data = {
"param": "block"
}
c.generic('GET', 'path', json.dumps(block_get_data), 'application/json')
You are facing this error because this dict
block_get_data = {
"param": "block"
}
you are trying to use it in this way
for key,val in block_get_data
and it will produce the error like
for key,val in block_get_data:
ValueError: too many values to unpack (expected 2)
It will be solved if your loop through dict by using .items() method.
for key,val in block_get_data.items():
I think by passing parameter as self.block_get_data.items() may solve your problem.
i had a problem when i was try to make the auto logout in odoo 10. I was created change password page, after change password, i want end session and back to login page again. But the result never land me to teh login page. Here my code :
#api.model
def create(self, vals):
res = super(MyPass, self).create(vals)
get_new_passwd = vals['new_password']
get_conf_passwd = vals['confirm_pwd']
if get_conf_passwd != get_new_passwd:
raise ValidationError ("Pass")
hashing_pass = CryptContext(['pbkdf2_sha512']).encrypt(get_new_passwd)
data_users = self.env['res.users'].browse(self.env.uid)
data_users.write({'password_crypt': hashing_pass})
request.session.logout(keep_db=True
I found the solution.
def check(self, **kw):
get_re_users = request.env.user
error = {}
if get_re_users.passwd_changed:
return werkzeug.utils.redirect('/web')
else:
if request.httprequest.method == 'POST':
get_new_passwd = http.request.params['new_password']
get_conf_passwd = http.request.params['confirm_pwd']
session = http.request.session
hashing_pass = CryptContext(['pbkdf2_sha512']).encrypt(get_new_passwd)
data_users = http.request.env['res.users'].sudo().browse(http.request.env.uid)
data_users.write({'password_crypt': hashing_pass, "passwd_changed":True, "name":get_username_odoo, "login":get_name_login})
if session.db and session.uid:
session.logout(keep_db=True)
# return {
# 'type' : 'ir.actions.act_url',
# 'url': '/web',
# 'target': 'self',
# }
return werkzeug.utils.redirect('/web/login')
return http.request.render('name_model.id_view')
I have an intro.html rendering view here:
def home(request):
user = request.user
small = user.username.title()
cases = Case.objects.filter(users=user).order_by('-english')
groups = user.groups.all()
allgroups = Group.objects.all()
suggestgroups = set(allgroups).difference(set(groups))
allusers = User.objects.all().exclude(username=user.username)
if not user.is_superuser:
user_ex = UserEx.objects.get(user=request.user)
friendlist = FriendList.objects.get(user=user_ex)
friends = friendlist.friends.all().exclude(username=user.username)
friendrequest = FriendReqRecList.objects.get(user=user_ex)
friendrequestsent = FriendReqSentList.objects.get(user=user_ex)
friendrequests = friendrequest.friend_rec_requests.all().exclude(username=user.username)
friendrequestsents = friendrequestsent.friend_sent_requests.all().exclude(username=user.username)
nonfriends = set(allusers).difference(set(friends))
return render(request, 'intro.html',
{'allusers': allusers,
'cases': cases,
'friendrequests': friendrequests,
'friendrequestsents': friendrequestsents,
'friends': friends,
'groups': groups,
'nonfriends': nonfriends,
'small' : small,
'suggestgroups': suggestgroups})
return render(request, 'intro.html', {'suggestgroups': suggestgroups,
'cases': cases,
'groups': groups,
'small' : small})
I have another share view which adds friends and groups to the intro.html (the change is only extra two parameters, sendfriends and sendgroups).
#login_required
def share(request):
user = request.user
small = user.username.title()
cases = Case.objects.filter(users=user).order_by('-english')
groups = user.groups.all()
allgroups = Group.objects.all()
suggestgroups = set(allgroups).difference(set(groups))
allusers = User.objects.all().exclude(username=user.username)
sendgroups = groups
if not user.is_superuser:
user_ex = UserEx.objects.get(user=request.user)
friendlist = FriendList.objects.get(user=user_ex)
friends = friendlist.friends.all().exclude(username=user.username)
friendrequest = FriendReqRecList.objects.get(user=user_ex)
friendrequestsent = FriendReqSentList.objects.get(user=user_ex)
friendrequests = friendrequest.friend_rec_requests.all().exclude(username=user.username)
friendrequestsents = friendrequestsent.friend_sent_requests.all().exclude(username=user.username)
nonfriends = set(allusers).difference(set(friends))
sendfriends = friendlist.friends.all().exclude(username=user.username)
return render(request, 'intro.html',
{'sendfriends': sendfriends,
'sendgroups': sendgroups,
'allusers': allusers,
'cases': cases,
'friendrequests': friendrequests,
'friendrequestsents': friendrequestsents,
'friends': friends,
'groups': groups,
'nonfriends': nonfriends,
'small' : small,
'suggestgroups': suggestgroups})
return render(request, 'intro.html', {'suggestgroups': suggestgroups,
'cases': cases,
'groups': groups,
'small' : small})
The second view also renders intro.html with extra two parameters (sendfriends and sendgroups). But I want to obey the don't repeat yourself (DRY) rule and use redirect() here. How can I pass these two parameters and redirect to home view over share view?
What about this:
def home(request):
return render_intro(request)
#login_required
def share(request):
return render_intro(request, is_share=True)
def render_intro(request, is_share=False):
context = intro_context(request.user, is_share)
return render(request, 'intro.html', context)
def intro_context(user, is_share=False):
small = user.username.title()
cases = Case.objects.filter(users=user).order_by('-english')
groups = user.groups.all()
allgroups = Group.objects.all()
suggestgroups = set(allgroups).difference(set(groups))
allusers = User.objects.all().exclude(username=user.username)
if user.is_superuser:
return {'suggestgroups': suggestgroups,
'cases': cases,
'groups': groups,
'small' : small}
user_ex = UserEx.objects.get(user=user)
friendlist = FriendList.objects.get(user=user_ex)
friends = friendlist.friends.all().exclude(username=user.username)
friendrequest = FriendReqRecList.objects.get(user=user_ex)
friendrequestsent = FriendReqSentList.objects.get(user=user_ex)
friendrequests = friendrequest.friend_rec_requests.all().exclude(username=user.username)
friendrequestsents = friendrequestsent.friend_sent_requests.all().exclude(username=user.username)
nonfriends = set(allusers).difference(set(friends))
context = {'allusers': allusers,
'cases': cases,
'friendrequests': friendrequests,
'friendrequestsents': friendrequestsents,
'friends': friends,
'groups': groups,
'nonfriends': nonfriends,
'small' : small,
'suggestgroups': suggestgroups}
if is_share:
sendgroups = groups
sendfriends = friendlist.friends.all().exclude(username=user.username)
context.update({'sendfriends': sendfriends,
'sendgroups': sendgroups})
return context
I can pass context to another class of models.Model but in models.AbstractModel it return none.
Here is my code.
class PrintWizard(models.TransientModel):
_name = 'print.report.wizard'
start_date = fields.Date()
end_date = fields.Date()
#api.multi
def print_report(self):
ctx = self.env.context.copy()
ctx.update({'start': self.start_date, 'end': self.end_date})
return self.env['report'].with_context(ctx).get_action('erp.report_id')
class ReportClass(models.AbstractModel):
_name = 'report.erp.report_id'
#api.multi
def render_html(self, data):
report_obj = self.env['report']
report = report_obj._get_report_from_name('erp.report_id')
start = self.env.context.get('start')
end = self.env.context.get('end')
docs = self.env['erp.account'].search([('start_date','>=',start),('end_date', '<=', end)])
docargs = {
'doc_model': report.model,
'docs': docs
}
return report_obj.render('erp.report_id', docargs)
i tried to print the start and end, it return none i think i am passing the context to absractmodel in a improper way.
Try this as an alternative to context (but it pretty well is context)
#api.multi
def print_report(self):
return {
'type':'ir.actions.report.xml',
'report_name': 'erp.report_id',
'datas': 'start': self.start_date, 'end': self.end_date}
}
#api.multi
def render_html(self, data):
report_obj = self.env['report']
report = report_obj._get_report_from_name('erp.report_id')
start = data.get('start')
end = data.get('end')
docs = self.env['erp.account'].search([('start_date','>=',start),('end_date', '<=', end)])
docargs = {
'doc_model': report.model,
'docs': docs
}
return report_obj.render('erp.report_id', docargs)
I have django project with 2 models:
class DeviceModel(models.Model):
name = models.CharField(max_length=255)
def __unicode__(self):
return self.name
class Device(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
device_model = models.ForeignKey(DeviceModel)
serial_number = models.CharField(max_length=255)
def __unicode__(self):
return self.device_model.name + " - " + self.serial_number
There many devices in the database and I want to plot chart "amount of devices" per "device model".
I'm trying to do this task with django chartit.
Code in view.py:
ds = PivotDataPool(
series=[
{'options': {
'source':Device.objects.all(),
'categories':'device_model'
},
'terms':{
u'Amount':Count('device_model'),
}
}
],
)
pvcht = PivotChart(
datasource=ds,
series_options =
[{'options':{
'type': 'column',
'stacking': True
},
'terms':[
u'Amount']
}],
chart_options =
{'title': {
'text': u'Device amount chart'},
'xAxis': {
'title': {
'text': u'Device name'}},
'yAxis': {
'title': {
'text': u'Amount'}}}
)
return render(request, 'charts.html', {'my_chart': pvcht})
This seems to plot result I need, but instead of device names it plots values of ForeignKey (1,2,3,4...) and I need actual device model names.
I thought that solution is to change 'categories' value to:
'categories':'device_model__name'
But this gives me error:
'ManyToOneRel' object has no attribute 'parent_model'
This type of referencing should work accoring to official example http://chartit.shutupandship.com/demo/pivot/pivot-with-legend/
What am I missing here?
C:\Anaconda\lib\site-packages\django\core\handlers\base.py in get_response
response = middleware_method(request, callback, callback_args, callback_kwargs)
if response:
break
if response is None:
wrapped_callback = self.make_view_atomic(callback)
try:
response = wrapped_callback(request, *callback_args, **callback_kwargs) ###
except Exception as e:
# If the view raised an exception, run it through exception
# middleware, and if the exception middleware returns a
# response, use that. Otherwise, reraise the exception.
for middleware_method in self._exception_middleware:
response = middleware_method(request, e)
D:\django\predator\predator\views.py in charts
series=[
{'options': {
'source':Device.objects.all(),
'categories':'device_model__name'
},
#'legend_by': 'device_model__device_class'},
'terms':{
u'Amount':Count('device_model'), ###
}
}
],
#pareto_term = 'Amount'
)
C:\Anaconda\lib\site-packages\chartit\chartdata.py in __init__
'terms': {
'asia_avg_temp': Avg('temperature')}}]
# Save user input to a separate dict. Can be used for debugging.
self.user_input = locals()
self.user_input['series'] = copy.deepcopy(series)
self.series = clean_pdps(series) ###
self.top_n_term = (top_n_term if top_n_term
in self.series.keys() else None)
self.top_n = (top_n if (self.top_n_term is not None
and isinstance(top_n, int)) else 0)
self.pareto_term = (pareto_term if pareto_term in
self.series.keys() else None)
C:\Anaconda\lib\site-packages\chartit\validation.py in clean_pdps
def clean_pdps(series):
"""Clean the PivotDataPool series input from the user.
"""
if isinstance(series, list):
series = _convert_pdps_to_dict(series)
clean_pdps(series) ###
elif isinstance(series, dict):
if not series:
raise APIInputError("'series' cannot be empty.")
for td in series.values():
# td is not a dict
if not isinstance(td, dict):
C:\Anaconda\lib\site-packages\chartit\validation.py in clean_pdps
try:
_validate_func(td['func'])
except KeyError:
raise APIInputError("Missing 'func': %s" % td)
# categories
try:
td['categories'], fa_cat = _clean_categories(td['categories'],
td['source']) ###
except KeyError:
raise APIInputError("Missing 'categories': %s" % td)
# legend_by
try:
td['legend_by'], fa_lgby = _clean_legend_by(td['legend_by'],
td['source'])
C:\Anaconda\lib\site-packages\chartit\validation.py in _clean_categories
else:
raise APIInputError("'categories' must be one of the following "
"types: basestring, tuple or list. Got %s of "
"type %s instead."
%(categories, type(categories)))
field_aliases = {}
for c in categories:
field_aliases[c] = _validate_field_lookup_term(source.model, c) ###
return categories, field_aliases
def _clean_legend_by(legend_by, source):
if isinstance(legend_by, basestring):
legend_by = [legend_by]
elif isinstance(legend_by, (tuple, list)):
C:\Anaconda\lib\site-packages\chartit\validation.py in _validate_field_lookup_term
# and m2m is True for many-to-many relations.
# When 'direct' is False, 'field_object' is the corresponding
# RelatedObject for this field (since the field doesn't have
# an instance associated with it).
field_details = model._meta.get_field_by_name(terms[0])
# if the field is direct field
if field_details[2]:
m = field_details[0].related.parent_model ###
else:
m = field_details[0].model
return _validate_field_lookup_term(m, '__'.join(terms[1:]))
def _clean_source(source):
In categories you can only use fields that are included in source queryset.
On the other hand in terms you can use foreignKey or manyTomany connected fields.
Find an example below.
Instead of using
'source':Device.objects.all()
'categories':'device_model'
try to use
'source':DeviceModel.objects.all()
'categories':'name'
and next
'Amount':Count('device__device_model')
I think there is a problem with newer version of django (1.8).
This code is deprecated:
m = field_details[0].related.parent_model
instead of it use
m = field_details[0].getattr(field.related, 'related_model', field.related.model)
You can also find fix to this problem on GitHub.
Hope this helps.