Make progress with automatic form field for model autocompletion

This commit is contained in:
Mario Hüttel 2021-12-31 22:29:47 +01:00
parent 258fd1747c
commit a0775a69ca
5 changed files with 97 additions and 17 deletions

View File

@ -3,15 +3,56 @@ from django.forms import widgets
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from parts import models as parts_models from parts import models as parts_models
from shimatta_modules.EngineeringNumberConverter import EngineeringNumberConverter from shimatta_modules.EngineeringNumberConverter import EngineeringNumberConverter
import uuid
class AutoCompleteWidget(widgets.Input):
template_name = 'widgets/autocomplete-foreign-key.html'
def __init__(self, api_search_url, image_field_name, foreign_model, *args, **kwargs):
super().__init__(*args, **kwargs)
self.image_field_name = image_field_name
self.foreign_model = foreign_model
self.api_search_url = api_search_url
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
try:
instance = self.foreign_model.objects.get(id=uuid.UUID(value))
except Exception as ex:
print(ex)
instance = None
image = None
if instance is not None:
image = getattr(instance, self.image_field_name)
class AutoCompleteWidget(widgets.TextInput): context['custom'] = {
template_name = 'widgets/autrocomplete-foreign-key.html' 'search_url': self.api_search_url,
'image_field_name': self.image_field_name,
'current_instance': instance,
'image': image,
}
return context
class AutocompleteForeingKeyField(forms.UUIDField): class AutocompleteForeingKeyField(forms.UUIDField):
def __init__(self, foreign_model=None, api_search_url=None, autocomplete_media_div=False, **kwargs): def __init__(self, foreign_model=None, api_search_url=None, image_field_name='image', **kwargs):
kwargs['widget'] = AutoCompleteWidget
super().__init__(**kwargs) super().__init__(**kwargs)
self.widget = AutoCompleteWidget(api_search_url, image_field_name, foreign_model)
self.foreign_model = foreign_model
def clean(self, value):
pre_cleaned_uuid = super().clean(value)
if pre_cleaned_uuid is None and not self.required:
return None
try:
obj = self.foreign_model.objects.get(id=pre_cleaned_uuid)
except self.foreign_model.DoesNotExist:
raise ValidationError('Given element does not exist')
return obj
class MyTestForm(forms.Form): class MyTestForm(forms.Form):
pass pass
@ -118,8 +159,9 @@ class AddStockForm(forms.Form):
new_stock.save() new_stock.save()
class ComponentForm(forms.ModelForm): class ComponentForm(forms.ModelForm):
manufacturer = AutocompleteForeingKeyField(widget=AutoCompleteWidget) manufacturer = AutocompleteForeingKeyField(api_search_url='foo', foreign_model=parts_models.Manufacturer)
component_type = AutocompleteForeingKeyField() component_type = AutocompleteForeingKeyField(api_search_url='bar', foreign_model=parts_models.ComponentType, required=False, image_field_name=None)
package = AutocompleteForeingKeyField(api_search_url='pkgurl', foreign_model=parts_models.Package, required=False)
class Meta: class Meta:
model = parts_models.Component model = parts_models.Component
fields = '__all__' fields = '__all__'

View File

@ -521,7 +521,7 @@ class ComponentDetailView(LoginRequiredMixin, BaseTemplateMixin, DetailView):
context['edit_form'] = EditComponentForm(instance=self.object) context['edit_form'] = EditComponentForm(instance=self.object)
context['stocks'] = Stock.objects.filter(component=self.object) context['stocks'] = Stock.objects.filter(component=self.object)
context['comp_form'] = ComponentForm() context['comp_form'] = ComponentForm(instance=self.object)
return context return context
@ -546,10 +546,25 @@ class ComponentDetailView(LoginRequiredMixin, BaseTemplateMixin, DetailView):
context['edit_form'] = form context['edit_form'] = form
return self.render_to_response(context) return self.render_to_response(context)
def handle_submit_edit_component_post(self, request, **kwargs):
context = self.get_context_data(**kwargs)
cform = ComponentForm(instance=self.object, data=request.POST, files=request.FILES)
if cform.is_valid():
print('valid')
cform.save()
else:
print('invalid')
context['comp_form'] = cform
return self.render_to_response(context)
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
self.object = self.get_object() self.object = self.get_object()
if 'submit-edit-comp' in request.POST: if 'submit-edit-comp' in request.POST:
return self.handle_submit_edit_post(request, **kwargs) return self.handle_submit_edit_post(request, **kwargs)
if 'submit-edit-component' in request.POST:
return self.handle_submit_edit_component_post(request, **kwargs)
return super().post(request, *args, **kwargs) return super().post(request, *args, **kwargs)

View File

@ -119,9 +119,11 @@
</div> </div>
</div> </div>
</div> </div>
<form action="" method="post">
{{comp_form|crispy}} {% csrf_token %}
{{comp_form|crispy}}
<input type="submit" name="submit-edit-component" value="Submit">
</form>
</div> </div>
{% if component.get_resolved_image %} {% if component.get_resolved_image %}

View File

@ -0,0 +1,28 @@
<div class="dropdown">
<input autocomplete="off" data-ac-url="{{custom.search_url}}" data-bs-toggle="dropdown" class="form-control{% if widget.errors %} is-invalid{% endif %}" type="text" placeholder="Search...">
<div class="d-flex align-items-center" id="{{widget.attrs.id}}-dflex-container">
{% if custom.current_instance %}
{% if custom.image_field_name %}
<div class="flex-shrink-0">
{% if custom.image %}
<img src="{{custom.image.url}}" style="max-width:64px;max-height:64px;" class="mr-3">
{% else %}
{% load static %}
<img src="{% static 'css/icons/card-image.svg' %}" style="width:64px;max-height:64px;" class="mr-3">
{% endif %}
</div>
{% endif %}
{% endif %}
<div class="flex-grow-1 ms-3">
{% if custom.current_instance %}
{{custom.current_instance}}
{% else %}
<span class="text-secondary">None selected</span>
{% endif %}
</div>
</div>
<ul id="{{widget.attrs.id}}-ac-ul" class="dropdown-menu">
</ul>
<input type="hidden" {% if widget.value != None %}value="{{widget.value}}"{%endif%} name={{widget.name}}>
</div>

View File

@ -1,7 +0,0 @@
<div class="dropdown">
<input autocomplete="off" data-bs-toggle="dropdown" class="form-control{% if widget.errors %} is-invalid{% endif %}" type="text" {% if widget.value != None %}value="{{widget.value}}"{%endif%}
{% for name, value in widget.attrs.items %}{% if value is not False and name != 'id' %} {{ name }}{% if value is not True %}="{{ value|stringformat:'s' }}"{% endif %}{% endif %}{% endfor %} id="{{widget.attrs.id}}_search_field">
<ul id="{{form.manufacturer.id_for_label}}-ac-ul" class="dropdown-menu">
</ul>
<input type="hidden" value="" name={{widget.name}}>
</div>