ModelSelect2TagWidget tagging new values
Mergpijp opened this issue · comments
Describe the bug
Clearly given 4 arguments but it says 5 were given..
Exception & Traceback
value_from_datadict() takes 4 positional arguments but 5 were given
values = super().value_from_datadict(self, data, files, name) at line 205
Code Snippet
From the documentation in forms.py:
class KeywordSelect2TagWidget(ModelSelect2TagWidget):
queryset = Keyword.objects.all()
def value_from_datadict(self, data, files, name):
values = super().value_from_datadict(self, data, files, name)
queryset = self.get_queryset()
pks = queryset.filter(**{'pk__in': list(values)}).values_list('pk', flat=True)
cleaned_values = []
for val in values:
if force_text(val) not in pks:
val = queryset.create(title=val).pk
cleaned_values.append(val)
return cleaned_values
In forms.py:
keywords = forms.ModelMultipleChoiceField(widget=KeywordSelect2TagWidget(
model=Keyword,
search_fields=['name__icontains',],
attrs={'data-minimum-input-length': 0,},
),queryset=Keyword.objects.all(), required=False)
class <class 'publications.forms.KeywordSelect2TagWidget'> data {'ISBN_number': '', 'collection_country': '', 'collection_date': '', 'collection_venue_and_city': '', 'contact_email': '', 'contact_telephone_number': '', 'contact_website': '', 'content_description': '', 'copyrights': 'unknown', 'csrfmiddlewaretoken': 'AzfnVs88SWyp5j1BO71TpD5dHBfHEbKK1dvJaSDuRGGmws0jaU3PydWitibwU5z4', 'description_of_illustration': '', 'donor': '', 'editor': '', 'extra_info': '', 'general_comments': '', 'is_a_translation': '', 'keywords': 'm', 'nr_of_pages': '', 'printed_by': '', 'publication_city': '', 'publication_date': '', 'published_by': '', 'publishing_organisation': '', 'save': 'Save', 'team_comments': '', 'title_original': '', 'title_subtitle_European': '', 'title_subtitle_transcription': '', 'title_translation': '', 'translated_from': ''} files <MultiValueDict: {}> name 'keywords' self <publications.forms.KeywordSelect2TagWidget object at 0x00000287D6A72780>
To Reproduce
Add new tags or existing tags to the keywords field.
Expected behavior
Expected it to work with existing keywords but the error persists.
Hi @Mergpijp thanks for the detailed error report. This isn't a bug though. It's a simple Python mistake. You don't pass self
to a super
call, that is done automatically by the interpreter, same as you don't have to add self
on a regular method call on an object instance.
Following your example:
values = super().value_from_datadict(data, files, name)
Dear Joe,
I tried running it without the self argument but it is still not working.
I will show you my model:
class Keyword(models.Model):
name = models.CharField(max_length=255, blank=True)
def __str__(self):
return self.name
I even tried to change title to name from the given code in the documentation.
val = queryset.create(name=val).pk
It will give : invalid literal for int() with base 10: 'hey' If i put 'hey' as tag.
Referring to:
pks = queryset.filter(**{'pk__in': list(values)}).values_list('pk', flat=True)
Any idea how to get this to work?
Hi @Mergpijp do could you provide the entire traceback please? It would help me to narrow down the issue. Best Joe
Hi @codingjoe, this time i tried entering 'something' here is the traceback:
Environment:
Request Method: POST
Request URL: http://127.0.0.1:8000/publication/new/
Django Version: 2.2.10
Python Version: 3.7.3
Installed Applications:
['publications',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'bootstrap4',
'crispy_forms',
'django_select2',
'countries_plus']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware']
Traceback:
File "C:\Users\Emmanuel\Documents\projects\globort\lib\site-packages\django\core\handlers\exception.py" in inner
34. response = get_response(request)
File "C:\Users\Emmanuel\Documents\projects\globort\lib\site-packages\django\core\handlers\base.py" in _get_response
115. response = self.process_exception_by_middleware(e, request)
File "C:\Users\Emmanuel\Documents\projects\globort\lib\site-packages\django\core\handlers\base.py" in _get_response
113. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Users\Emmanuel\Documents\projects\globort\lib\site-packages\django\contrib\auth\decorators.py" in _wrapped_view
21. return view_func(request, *args, **kwargs)
File "C:\Users\Emmanuel\Documents\projects\globort\lib\site-packages\django\views\generic\base.py" in view
71. return self.dispatch(request, *args, **kwargs)
File "C:\Users\Emmanuel\Documents\projects\globort\lib\site-packages\django\views\generic\base.py" in dispatch
97. return handler(request, *args, **kwargs)
File "C:\Users\Emmanuel\Documents\projects\globort\lib\site-packages\django\views\generic\edit.py" in post
172. return super().post(request, *args, **kwargs)
File "C:\Users\Emmanuel\Documents\projects\globort\lib\site-packages\django\views\generic\edit.py" in post
141. if form.is_valid():
File "C:\Users\Emmanuel\Documents\projects\globort\lib\site-packages\django\forms\forms.py" in is_valid
185. return self.is_bound and not self.errors
File "C:\Users\Emmanuel\Documents\projects\globort\lib\site-packages\django\forms\forms.py" in errors
180. self.full_clean()
File "C:\Users\Emmanuel\Documents\projects\globort\lib\site-packages\django\forms\forms.py" in full_clean
381. self._clean_fields()
File "C:\Users\Emmanuel\Documents\projects\globort\lib\site-packages\django\forms\forms.py" in _clean_fields
393. value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
File "C:\Users\Emmanuel\Documents\projects\GlobalOrthodoxy\publications\forms.py" in value_from_datadict
207. pks = queryset.filter(**{'pk__in': list(values)}).values_list('pk', flat=True)
File "C:\Users\Emmanuel\Documents\projects\globort\lib\site-packages\django\db\models\query.py" in filter
892. return self._filter_or_exclude(False, *args, **kwargs)
File "C:\Users\Emmanuel\Documents\projects\globort\lib\site-packages\django\db\models\query.py" in _filter_or_exclude
910. clone.query.add_q(Q(*args, **kwargs))
File "C:\Users\Emmanuel\Documents\projects\globort\lib\site-packages\django\db\models\sql\query.py" in add_q
1290. clause, _ = self._add_q(q_object, self.used_aliases)
File "C:\Users\Emmanuel\Documents\projects\globort\lib\site-packages\django\db\models\sql\query.py" in _add_q
1318. split_subq=split_subq, simple_col=simple_col,
File "C:\Users\Emmanuel\Documents\projects\globort\lib\site-packages\django\db\models\sql\query.py" in build_filter
1251. condition = self.build_lookup(lookups, col, value)
File "C:\Users\Emmanuel\Documents\projects\globort\lib\site-packages\django\db\models\sql\query.py" in build_lookup
1116. lookup = lookup_class(lhs, rhs)
File "C:\Users\Emmanuel\Documents\projects\globort\lib\site-packages\django\db\models\lookups.py" in __init__
20. self.rhs = self.get_prep_lookup()
File "C:\Users\Emmanuel\Documents\projects\globort\lib\site-packages\django\db\models\lookups.py" in get_prep_lookup
210. rhs_value = self.lhs.output_field.get_prep_value(rhs_value)
File "C:\Users\Emmanuel\Documents\projects\globort\lib\site-packages\django\db\models\fields\__init__.py" in get_prep_value
972. return int(value)
Exception Type: ValueError at /publication/new/
Exception Value: invalid literal for int() with base 10: 'something'
Hi @Mergpijp,
Ok, so what you need to do here, is to remove all strings from list of possible keys:
class KeywordSelect2TagWidget(ModelSelect2TagWidget):
queryset = Keyword.objects.all()
def value_from_datadict(self, data, files, name):
values = super().value_from_datadict(self, data, files, name)
queryset = self.get_queryset()
pks = queryset.filter(**{'pk__in': [v for v in values if v.isdigit())}).values_list('pk', flat=True)
cleaned_values = []
for val in values:
if force_text(val) not in pks:
val = queryset.create(title=val).pk
cleaned_values.append(val)
return cleaned_values
Hi @codingjoe ,
There was a typo in your code the list comprehension did not close. I have the following code:
class KeywordSelect2TagWidget(ModelSelect2TagWidget):
queryset = Keyword.objects.all()
def value_from_datadict(self, data, files, name):
values = super().value_from_datadict(data, files, name)
queryset = self.get_queryset()
pks = queryset.filter(**{'pk__in': [v for v in values if v.isdigit()]}).values_list('pk', flat=True)
cleaned_values = []
for val in values:
if force_text(val) not in pks:
val = queryset.create(name=val).pk
cleaned_values.append(val)
return cleaned_values
Now it works when I enter black and press save the form will have black as keyword. However if then enter white and save. It will have an integer as keyword and white. If enter Orange and save. It will have 2 integers and Orange as keyword. Something is still wrong.
Honestly, at this point, you a probably best served with a debugger. Maybe use pdb and set a breakpoint in the for loop. I am sorry, I can't be of better help.
Ok installed django-pdb with breakpoint() in the code. How do I inspect variables? I am using pycharm at the moment.
EDIT
Never mind i found out how.
Hi @codingjoe,
I solved this the only downside is that you cannot use integers as tag. Here is the code:
EDIT
there is no downside..
def represent_int(s):
try:
int(s)
return True
except ValueError:
return False
class KeywordSelect2TagWidget(ModelSelect2TagWidget):
queryset = Keyword.objects.all()
def value_from_datadict(self, data, files, name):
values = super().value_from_datadict(data, files, name)
print(values)
queryset = self.get_queryset()
pks = queryset.filter(**{'pk__in': [v for v in values if v.isdigit()]}).values_list('pk', flat=True)
cleaned_values = []
for val in values:
if represent_int(val) and int(val) not in pks or not represent_int(val) and force_text(val) not in pks:
val = queryset.create(name=val).pk
cleaned_values.append(val)
return cleaned_values
Thanks for all the help appreciate it!