Skip to content

Commit

Permalink
improve forms with django-autocomplete-light
Browse files Browse the repository at this point in the history
  • Loading branch information
kiancm committed Dec 31, 2020
1 parent e90a76c commit f8e581a
Show file tree
Hide file tree
Showing 10 changed files with 145 additions and 21 deletions.
26 changes: 24 additions & 2 deletions database/filters.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import django_filters
from dal import autocomplete
from django.db.models import Count

from .models import Species, Reaction, Source


class SpeciesFilter(django_filters.FilterSet):
speciesname__name = django_filters.CharFilter(
field_name="speciesname", lookup_expr="name", distinct=True, label="Species Name",
field_name="speciesname",
lookup_expr="name",
distinct=True,
label="Species Name",
)
isomers__formula__formula = django_filters.CharFilter(
field_name="isomers", lookup_expr="formula__formula", label="Formula"
Expand All @@ -23,7 +27,9 @@ class SpeciesFilter(django_filters.FilterSet):
label="Structure Adjacency List",
)
isomers__structure__multiplicity = django_filters.NumberFilter(
field_name="isomers", lookup_expr="structure__multiplicity", label="Structure Multiplicity",
field_name="isomers",
lookup_expr="structure__multiplicity",
label="Structure Multiplicity",
)

class Meta:
Expand All @@ -39,6 +45,10 @@ class ReactionFilter(django_filters.FilterSet):
field_name="species",
method="filter_reactant",
label="Reactant",
widget=autocomplete.ModelSelect2(
url="species-autocomplete",
attrs={"data-html": True},
),
)
reactant2 = django_filters.ModelChoiceFilter(
queryset=Species.objects.annotate(reaction_count=Count("reaction")).filter(
Expand All @@ -47,6 +57,10 @@ class ReactionFilter(django_filters.FilterSet):
field_name="species",
method="filter_reactant",
label="Reactant",
widget=autocomplete.ModelSelect2(
url="species-autocomplete",
attrs={"data-html": True},
),
)
product1 = django_filters.ModelChoiceFilter(
queryset=Species.objects.annotate(reaction_count=Count("reaction")).filter(
Expand All @@ -55,6 +69,10 @@ class ReactionFilter(django_filters.FilterSet):
field_name="species",
method="filter_product",
label="Product",
widget=autocomplete.ModelSelect2(
url="species-autocomplete",
attrs={"data-html": True},
),
)
product2 = django_filters.ModelChoiceFilter(
queryset=Species.objects.annotate(reaction_count=Count("reaction")).filter(
Expand All @@ -63,6 +81,10 @@ class ReactionFilter(django_filters.FilterSet):
field_name="species",
method="filter_product",
label="Product",
widget=autocomplete.ModelSelect2(
url="species-autocomplete",
attrs={"data-html": True},
),
)

def filter_reactant(self, queryset, name, value):
Expand Down
6 changes: 6 additions & 0 deletions database/forms.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from dal import autocomplete
from django.contrib.auth.forms import UserCreationForm
from django import forms
from django.forms import inlineformset_factory
Expand All @@ -14,6 +15,11 @@ class StoichiometryForm(forms.ModelForm):
class Meta:
model = Stoichiometry
fields = "__all__"
widgets = {
"species": autocomplete.ModelSelect2(
url="species-autocomplete", attrs={"data-html": True}
)
}

def has_changed(self):
return True
Expand Down
14 changes: 8 additions & 6 deletions database/templates/database/base.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{% load static %} {% load crispy_forms_tags %}
{% load static %}
{% load crispy_forms_tags %}

<!DOCTYPE html>
<html>
Expand Down Expand Up @@ -35,6 +36,12 @@
async
src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"
></script>
<script
src="https://code.jquery.com/jquery-3.5.1.slim.min.js"
integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj"
crossorigin="anonymous"
></script>
<script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script>
{% if title %}
<title>Kinetic Models Site - {{ title }}</title>
{% else %}
Expand Down Expand Up @@ -154,11 +161,6 @@ <h5>Source Lookup</h5>
</div>
</main>
<!-- Bootstrap JS-->
<script
src="https://code.jquery.com/jquery-3.5.1.slim.min.js"
integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj"
crossorigin="anonymous"
></script>
<script
src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.bundle.min.js"
crossorigin="anonymous"
Expand Down
2 changes: 2 additions & 0 deletions database/templates/database/reaction_filter.html
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
{% extends "database/base.html" %}

{% load crispy_forms_tags %}
{% load utils %}
{% load renders %}

{% block content %}
<h1>Reaction</h1>
<form action="" method="get">
{{ filter.form.media }}
{{ filter.form|crispy }}
<button type="submit" class="btn btn-primary">Submit</button>
</form>
Expand Down
1 change: 1 addition & 0 deletions database/templates/database/revision.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ <h2>{{ name }} {{ object.id }} Revision</h2>
{% csrf_token %}
{% if formset %}
{% for form in formset %}
{{ form.media }}
{{ form|crispy }}
{% endfor %}
{{ form|crispy }}
Expand Down
65 changes: 62 additions & 3 deletions database/templatetags/renders.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,8 @@ def _render_reaction_list_card(reaction):
)


@register.filter
def render_reaction_list(reactions):
cards = "\n".join(_render_reaction_list_card(r) for r in reactions)
def _render_card_list(card_render_func, objects):
cards = "\n".join(card_render_func(o) for o in objects)

return mark_safe(
"""
Expand All @@ -110,6 +109,11 @@ def render_reaction_list(reactions):
)


@register.filter
def render_reaction_list(reactions):
return _render_card_list(_render_reaction_list_card, reactions)


@register.simple_tag(takes_context=True)
def render_pagination(context, objects, page_name):
if objects.has_previous():
Expand Down Expand Up @@ -164,3 +168,58 @@ def render_pagination(context, objects, page_name):
objects=objects, prev=prev, nxt=nxt
)
)


def render_species_list_card(species):
if species.names:
names_inner = "\n".join(
f"<li class='list-group-item'>{name}</li>" for name in species.names
)
names_render = f"""
<h6>Names</h6>
<ul class="list-group">
{names_inner}
</ul>
<br />
""".strip()
else:
names_render = ""

if species.structures:
structures_inner = "\n".join(
f"""
<li class='list-group-item'>
<img src='{reverse('draw-structure', args=[structure.pk])}'/>
</li>
""".strip()
for structure in species.structures
)
structures_render = f"""
<h6>Structures</h6>
<ul class="list-group">
{structures_inner}
</ul>
""".strip()
else:
structures_render = ""

return mark_safe(
f"""
<a
href="{reverse('species-detail', args=[species.pk])}"
class="list-group-item list-group-item-action flex-column align-items-start"
>
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">{species.formula}</h5>
<small>ID: {species.pk}</small>
</div>
{names_render}
{structures_render}
</a>
""".strip()
)


@register.filter
def render_species_list(species):
return _render_card_list(render_species_list_card, species)
5 changes: 5 additions & 0 deletions database/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
urlpatterns = [
path(r"", views.BaseView.as_view(), name="home"),
path(r"register/", views.RegistrationView.as_view(), name="register"),
path(
r"species-autocomplete/",
views.SpeciesAutocomplete.as_view(),
name="species-autocomplete",
),
path(
r"species_search/",
views.SpeciesFilterView.as_view(),
Expand Down
44 changes: 34 additions & 10 deletions database/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from collections import defaultdict
from datetime import datetime

from dal import autocomplete
from django.contrib.auth import login
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.db import transaction
Expand Down Expand Up @@ -32,6 +33,7 @@
)
from .filters import SpeciesFilter, ReactionFilter, SourceFilter
from .forms import RegistrationForm, StoichiometryFormSet
from .templatetags.renders import render_species_list_card


class SidebarLookup:
Expand Down Expand Up @@ -399,16 +401,16 @@ def get_context_data(self, **kwargs):

def form_valid(self, form):
with transaction.atomic():
instance = self.save_form(form)
formset = self.get_formset()
objects = formset.save(commit=False)
for o in objects:
o.id = None
o.revision = True
o.created_by = self.request.user
o.created_on = datetime.now()
o.reaction = instance
o.save()
instance = self.save_form(form)
formset = self.get_formset()
objects = formset.save(commit=False)
for o in objects:
o.id = None
o.revision = True
o.created_by = self.request.user
o.created_on = datetime.now()
o.reaction = instance
o.save()

return HttpResponseRedirect(self.get_success_url())

Expand Down Expand Up @@ -467,3 +469,25 @@ def update_model(self, revision):
stoich.status = ""
stoich.reaction = reaction
stoich.save()


class SpeciesAutocomplete(autocomplete.Select2QuerySetView):
def get_queryset(self):
queryset = Species.objects.all()

if self.q:
for query in ["speciesname__name__istartswith", "isomers__formula", "id"]:
try:
filtered = queryset.filter(**{query: self.q})
if filtered:
return filtered
except ValueError:
continue

return queryset if not self.q else []

def get_result_label(self, item):
return render_species_list_card(item)

def get_selected_result_label(self, item):
return str(item)
1 change: 1 addition & 0 deletions environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ dependencies:
- flake8
- flake8-django
- django-test-migrations
- django-autocomplete-light
2 changes: 2 additions & 0 deletions kms/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@

INSTALLED_APPS = [
"database.apps.DatabaseConfig",
"dal",
"dal_select2",
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
Expand Down

0 comments on commit f8e581a

Please sign in to comment.