I have a large django model with about 800 objects and I want to create a view in which the user can select a certain number of those objects to pass to another view for further processing. The fact that there are so many objects of the model makes listing all the objects very unpractical, as the user would have to scroll through 800 objects.
In order to address this problem, I want to place an as-you-type search-bar in the top of the view so that the user can type the name of the objects and select them by clicking them. When the objects are selected, they should appear under the search-bar as tags that the user can remove by clicking an "x" next to each one.
When the user has made all the required selections, then they should be able to click a button and jump to the next view where those selected objects are accessible.
The model I am using can be simplified to:
class Song():
song_name = models.CharField(max_length=256)
song_author = models.CharField(max_length=256, blank=True)
song_content = models.TextField()
def __str__(self):
return self.song_name
class Meta:
ordering = ['song_order']
song_order = models.PositiveIntegerField(editable=False, db_index=True)
So far I have been able to make a view to search through the model.
mytemplate.html
<!DOCTYPE html>
{% load static %}
<html style="height: 100%;" lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="preload" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons" as="style" onload="this.rel='stylesheet'">
<link rel="preload" href="https://unpkg.com/bootstrap-material-design#4.1.1/dist/css/bootstrap-material-design.min.css" as="style" onload="this.rel='stylesheet'">
<link rel="stylesheet" href="{% static 'css/main.css' %}">
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
</head>
<body>
{% block body_block %}
<div class="container" style="padding-bottom:40px;margin-top: 35px;">
<div class="form-group">
<label for="searchInput" class="bmd-label-floating">Search</label>
<input type="text" class="form-control" id="searchInput" oninput="filter()">
</div>
<ul class="list-group bmd-list-group-sm">
{% for song in songs %}
<div
class="list-group-item"
data-title="{{song.song_name}}"
data-author="{{song.song_author}}"
data-lyrics="{{song.song_content}}">
<h4>
{{song.song_name}}
{% if song.song_author %}
({{ song.song_author }})
{% endif %}
</h4>
</div>
{% endfor %}
</ul>
</div>
<script>
$('#searchInput').focus();
function short(s) {
let punctuationRegEx = /[.,\/#!$%\^&\*;:{}=\-_`~()]/g;
return s.replace(punctuationRegEx, '')
.toLowerCase()
.normalize("NFD")
.replace(/[\u0300-\u036f]/g, "");
}
function filter() {
let f = short($('#searchInput').val());
$('.list-group-item').each(function (index) {
if (short($(this).data('title') + "").includes(f) ||
short($(this).data('author') + "").includes(f)
) {
$(this).show();
} else {
$(this).hide();
}
});
}
</script>
{% endblock %}
</body>
</html>
views.py
class SongListView(ListView):
context_object_name = 'songs'
model = Song
template_name = "songapp/mytemplate.html"
Any ideas on how to do the selection?
I have created a part inventory program with the as you type search.
It's a complete example of ajax call for search and database.
You can modify it to show the results under the search with the X.
https://github.com/edcodes/Django-Parts-Inventory
Related
So I have a form you can fill from the webpage which I want to save to my django database to create a new object when submitted instead of adding it in admin. I've tried it and the system works except for one thing, saving it, the function starts as I've managed to switch pages from that function when clicking submit but the object isnt found in the database after so I guess the saving is the only issue.
Here is my views.py :
def uploadform(request):
if request.method == 'POST':
name = request.POST['name']
details = request.POST['details']
littype = request.POST['littype']
image = request.POST['image']
new_object = PostForm.objects.create(name=name, details=details, littype=littype, image=image)
new_object.save()
return render(request, 'uploadform.html', {})
Here is my models.py (The one to save the form is the "PostForm" class) :
from email.mime import image
from django.db import models
from django.db.models import Model
# Create your models here.
class info(models.Model):
name = models.CharField(max_length=100)
details = models.CharField(max_length=10000)
littype = models.CharField(default="Type of Litterature", blank=True, max_length=100)
image = models.ImageField(default=None, blank=True, upload_to='app/files/covers')
class PostForm(models.Model):
name = models.CharField(max_length=200)
details = models.TextField()
littype = models.TextField()
image = models.FileField(upload_to='app/files/')
def __str__(self):
return self.name
Here is my html page :
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<link type="text/css" rel="stylesheet" href="templates/uploadform.css">
<link type="text/css" rel="stylesheet" href="templates/form.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Fascinate&family=Roboto:wght#400;500;700&display=swap" rel="stylesheet">
</head>
<body style="height: 1000px;">
<div class="page-title">
<h1 class="page-title-text">Book & Movie & Series Recommendations</h1>
</div>
<div class="page-title-add-button-div">
<a class="page-title-add-button-link" href="/"> Home </a>
</div>
<div>
<form method="POST" action="{% url 'uploadform' %}" enctype="multipart/form-data">
{% csrf_token %}
<input type="text" id="name" name="name" placeholder="Title">
<input type="text" id="details" name="details" placeholder="Summary Details">
<input type="text" id="littype" name="littype" placeholder="Type of Literature">
<input type="file" id="image" name="image" placeholder="Cover Picture">
<input type="submit" value="Submit">
</form>
</div>
</body>
</html>
Any ideas? Thanks for your time and answers! Have a good day!
This is a quick answer to anyone wondering how I fixed the issue. First off, I had a few things I had to correct which Adam helped me find in the comments but the main issue was when I was creating a new object to my database instead of saving from the "info" model which looks exactly the same and is found in my database i was saving from the "PostForm" model which cannot be found in the database.
I hope this helps someone and I'll try to stay active and answer any questions if you have any.
I want to create different templates, but both of them share almost the same attributes. For example, both the blog page and the movie page have the model 'Post' that allows users to add new Articles/Movies to the Website.
Also, I assume that the 'Movie - Post' model would need different fields than the 'Article - Post' model, but they still share many of them.
Someone can help me how to implement that out?
There are two possible ways to do this.
1. Make an outer file and insert all your code into the inner file. This uses extends.
The outer file, base.html
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href = "https://code.jquery.com/ui/1.10.4/themes/ui-lightness/jquery-ui.css"
rel = "stylesheet">
</head>
<body>
<p> You can also put anything else you want on every page here.</p>
<main role="main" class="container">
{% block content %}
{% endblock %}
</main>
</div>
</body>
</html>
The inner file, page.html
{% extends 'base.html' %}
{% block content %}
<p>This is the stuff you want to be unique to the page. </p>
{% endblock %}
The disadvantage of this method is that you are essentially putting one page inside another and it is a little less flexible.
2. Include the chunk of code inside an HTML file. This uses include.
The page you want to add the header to, page.html
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href = "https://code.jquery.com/ui/1.10.4/themes/ui-lightness/jquery-ui.css"
rel = "stylesheet">
</head>
<style>
body { padding-top: 70px;
font-family: Poppins;
}
</style>
<body>
{% include "header.html" %}
<main role="main" class="container">
<p> Whatever you want on your page</p>
</main>
</div>
</body>
</html>
The actual code for your header, header.html
<div class="header">
<p>My supercool header</p>
</div>
Include will insert that block of HTML code right where you put the include statement.
With regard to the models with common elements, you can do something like this:
from django.db import models
class CommonInfo(models.Model):
#common fields go here
post = models.CharField(max_length=100)
class Meta:
abstract = True
class Blog(CommonInfo):
#fields unique to blog go here
name = models.CharField(max_length=5)
class Movie(CommonInfo):
#fields unique to movie go here
name = models.CharField(max_length=5)
So the thing is that I added custom user model
models.py
class CustomUserModel(AbstractUser):
pass
Morasko = "Morasko"
Piatkowo = "Piątkowo"
district_choices = [
(Morasko, "Morasko"),
(Piatkowo, "Piątkowo"),
]
district = models.CharField(max_length=15, choices=district_choices, default=district_choices[0])
Field 'district' is supposed to be a choice field with two options. Here is custom registration form I am using.
forms.py
class NewUserForm(UserCreationForm):
district = forms.ChoiceField(widget=forms.RadioSelect, choices=CustomUserModel.district_choices)
class Meta(UserCreationForm):
model = CustomUserModel
fields = ('username', 'email', 'district', 'password1', 'password2')
def save(self, commit=True):
user = super(NewUserForm, self).save(commit=False)
user.email = self.cleaned_data['email']
user.district = self.cleaned_data['district']
if commit:
user.save()
return user
My html registration file:
register.html
{% extends "map_neigh/home.html" %}
{% block content %}
<div class='container'>
<div id='inner'>
<br>
<form method="POST">
{% csrf_token %}
{{form.as_p}}
<button class="btn btn-primary" type="submit">Register</button>
</form>
<br>
<br>
If you already have an account <strong>log in.</strong>
</div>
</div>
{% endblock %}
Choice field actually works - users are saved in DB with clicked district, but it looks inactive and I have no clue why. Clicking it doesn't change the appearance, if I hove over one of options pointer doesn't change neither.
Below screenshot of the registration form.
home.html
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title>NoFence</title>
{% load static %}
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<link rel = "stylesheet" type = "text/css" href = "{% static 'style.css' %}"/>
<!--bootstrap-->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<!--jQuery links -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script src="https://code.jquery.com/jquery-1.7.1.min.js"></script>
<!--leaflet links -->
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet/v0.7.7/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet/v0.7.7/leaflet.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet.locatecontrol#0.65.2/dist/L.Control.Locate.min.css" />
<script src="https://cdn.jsdelivr.net/npm/leaflet.locatecontrol#0.65.2/src/L.Control.Locate.min.js" charset="utf-8"></script>
<!--toast-->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
<!--jquery scripts -->
<script>
</script>
</head>
<body>
<!--navigation bar-->
{% include "map_neigh/includes/navbar.html" %}
<!--user messages-->
{% include "map_neigh/includes/messaging.html" %}
{% block content %}
{% endblock %}
</body>
</html>
style.css
body {
margin:0;
padding:0;
background-color:#808080;
}
#menu_res {
position: relative;
padding-left: 15%;
font-size: 1.25vw;
}
#menu {
position: relative;
padding-left: 15%;
padding-top: 1.5vw;
padding-bottom: 0.9vw;
font-size: 1.5vw;
}
html{
scroll-behavior: smooth;
}
#inner {
width: 50%;
margin: 0 auto;
}
Try this
models.py
class CustomUserModel(AbstractUser):
Morasko = "Morasko"
Piatkowo = "Piątkowo"
district_choices = (
(Morasko, "Morasko"),
(Piatkowo, "Piątkowo"),
)
district = models.CharField(
max_length=15,
choices=district_choices,
default=Morasko)
In your forms.py, your UserCreationForm needs to be a ModelForm, or you can just directly define it like this.
forms.py
class NewUserForm(forms.ModelForm):
class Meta:
model = CustomerUserModel
fields = ('username', 'email', 'district', 'password1', 'password2')
widgets = {
'district': forms.RadioSelect,
}
I have a django (2.0) application with a page that contains two separate forms that are not meant to interact with each other at all. However, I am getting some weird behavior. Pictured here, when I click on one of the labels on the bottom form, it triggers the checkbox for the corresponding (based on row, not name) element in the upper form (clicking "members" on the group form selected "Email" on the person form). This only goes one way--clicking anywhere on the top form never effects the bottom form. Checking the actual boxes of the bottom form do trigger the expected boxes that their labels correspond to.
The html for this page is:
<head>
{% load static %}
<title>LDAP - Search</title>
<link rel="shortcut icon" href="{% static 'djangoWrapper/favicon.ico' %}">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" integrity="sha384-WskhaSGFgHYWDcbwN70/dfYBj47jz9qbsMId/iRN3ewGhXQFZCSftd1LZCfmhktB" crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="{% static 'ldap/style.css' %}">
</head>
<body>
<h1>Search</h1>
<div class="search-form">
<h2>Search for Person</h2>
<form action="{% url 'ldap:person_results' %}" method='post'>
{% csrf_token %}
<table>
{{ personForm }}
</table>
<input type="submit" value="Search">
</form>
</div>
<div class="search-form">
<h2>Search for Group</h2>
<form action="{% url 'ldap:group_results' %}" method='post'>
{% csrf_token %}
<table>
{{ groupForm }}
</table>
<input type="submit" value="Search" />
</form>
</div>
<div class="url">
or go back to login
</div>
</body>
And the forms are
class PersonSearchForm(forms.Form):
uniqname = forms.CharField(label="uniqname", max_length=10)
options = personFieldsDict.items()
attributes = forms.MultipleChoiceField(
widget=forms.CheckboxSelectMultiple, choices = options, label='',
required=False)
class GroupSearchForm(forms.Form):
groupName = forms.CharField(label='Group Name')
options = groupFieldsDict.items()
attributes = forms.MultipleChoiceField(
widget=forms.CheckboxSelectMultiple, choices = options, label='',
required=False)
And the view that renders this page is simply:
def search(request):
personForm = PersonSearchForm()
groupForm = GroupSearchForm()
context = {'personForm': personForm, 'groupForm': groupForm}
return render(request, 'ldap/search.html', context)
I am guessing that this has something to do with the fact that both forms use a MultipleChoiceField widget, but I can't see how they are interacting with each other if their attributes are different and they are in different div's. Any idea on why there is this interaction? Thanks.
You should use the prefix argument to one or both forms to prevent the fields from interferin with each other.
personForm = PersonSearchForm(prefix="person")
groupForm = GroupSearchForm(prefix="group")
Don't forget to also use the prefixes when instantiating the form on post.
I am now studying Django form. Right now I am focusing on crispy form. For now crispy and then after I master the form I will move on to Django Admin form and Django admin model form.
Django 1.10
Python 3.6.0
I am following these tutorials:
https://blog.bixly.com/awesome-forms-django-crispy-forms
http://django-crispy-forms.readthedocs.io/en/latest/layouts.html#
https://godjango.com/29-crispy-forms/
Here are my source code:
views.py:
from django.views.generic import FormView
from apps.colors.forms import PersonDetailForm
class ColorStudyView(FormView):
template_name = 'colors/study.html'
form_class = PersonDetailForm
success_url = '/'
forms.py:
from crispy_forms.bootstrap import Tab, TabHolder
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit, Layout
from django import forms
class NoFormTagCrispyFormMixin(object):
#property
def helper(self):
if not hasattr(self, '_helper'):
self._helper = FormHelper()
self._helper.form_tag = False
return self._helper
class PersonDetailForm(forms.Form):
name = forms.CharField(max_length=100)
age = forms.IntegerField(required=False)
address1 = forms.CharField(max_length=50, required=False)
address2 = forms.CharField(max_length=50, required=False)
city = forms.CharField(max_length=50, required=False)
state = forms.CharField(max_length=50, required=False)
mobile = forms.CharField(max_length=32, required=False)
home = forms.CharField(max_length=32, required=False)
office = forms.CharField(max_length=32, required=False)
twitter = forms.CharField(max_length=100, required=False)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.layout = Layout(
TabHolder(
Tab('Information',
'name',
'age'
),
Tab('Address',
'address1',
'address2',
'city',
'state'
),
Tab('Contact',
'mobile',
'home',
'office',
'twitter',
)
)
)
self.helper.layout.append(Submit('submit', 'Submit'))
study.html:
{% load crispy_forms_tags %}
<!DOCTYPE html>
<html lang="en">
<head>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet" />
<meta charset="UTF-8">
<title>Study</title>
<form action="" method="POST">
{% crispy form %}
</form>
</head>
<body>
</body>
</html>
Problem:
Tab does not change.
Am I miss something?
Sorry for very basic level question, but this is my first day with Django frontend
Here is my picture. Tab Address and Contact are not work.
Update:
Zollie solves my problem. Here is my study.html
{% load staticfiles %}
{% load crispy_forms_tags %}
<!DOCTYPE html>
<html lang="en">
<head>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet" />
<meta charset="UTF-8">
<title>Study</title>
<form action="" method="POST">
{% crispy form %}
</form>
</head>
<body>
<script src="http://code.jquery.com/jquery-1.9.1.js">
</script>
<script src="http://code.jquery.com/ui/1.11.0/jquery-ui.js">
</script>
<script type="text/javascript" src="{% static 'bootstrap-tab.js' %}"> </script>
</body>
</html>
And here is my filesystem configuration
/Users/el/Code/siam-sbrand/static
(siam-sbrand) Sarits-MacBook-Air-2:static el$ ls
admin django_extensions img
bootstrap-tab.js file.txt js
dist font rest_framework
I had the same problem when first I wanted to use the Tabs in crispy forms. The documentation is quite weak unfortunately for this package.
The answer on why TabHolder and Tabs are not working is, because you have to include jquery and javascript in your html template head. Also, you have to download bootstrap-tab.js and put it in your 'static' folder in the bootstrap subfolder ie., and you also have to include the path to it in your html .
So, it is not enough if you just include Bootstrap or bootstrap.css in the html head.
Here is an example how it should look like:
<head>
<script src="http://code.jquery.com/jquery-1.9.1.js">
</script>
<script src="http://code.jquery.com/ui/1.11.0/jquery-ui.js">
</script>
<script type="text/javascript" src="{% static 'bootstrap/js/bootstrap-tab.js' %}"> </script>
Check these things:
In settings.py you should have CRISPY_TEMPLATE_PACK = 'bootstrap3'
In your static files you should have a bootstrap-tab.js
Although the question is old and solved, I find it might be worthwhile to add some more observations: I came across the problem and found another cause.
First, I checked and confirmed that I had included the .js files as mentioned in the accepted answer. But the tab still did not work.
I found some bootstrap tab examples, such as https://mdbootstrap.com/docs/jquery/components/tabs/. And I concluded that, to make tab work, one should make sure there is a one-to-one relationship between the href attr of the 'tab header' and the id attr of the 'tab body'. Like this:
<ul class="nav nav-tabs" id="myTab" role="tablist">
<li class="nav-item">
<a class="nav-link active" id="home-tab" data-toggle="tab" href="#home" role="tab" aria-controls="home"
aria-selected="true">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" id="profile-tab" data-toggle="tab" href="#profile" role="tab" aria-controls="profile"
aria-selected="false">Profile</a>
</li>
<li class="nav-item">
<a class="nav-link" id="contact-tab" data-toggle="tab" href="#contact" role="tab" aria-controls="contact"
aria-selected="false">Contact</a>
</li>
</ul>
<div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="home" role="tabpanel" aria-labelledby="home-tab">A Tab</div>
<div class="tab-pane fade" id="profile" role="tabpanel" aria-labelledby="profile-tab">B tab</div>
<div class="tab-pane fade" id="contact" role="tabpanel" aria-labelledby="contact-tab">C tab</div>
</div>
Note: pay attention to the href attrs of 'a' elements and the id attrs of 'div' elements.
I did some experimentation and concluded that: once related .js and other files are imported and href and id attrs are properly set, the tab would work.
Now the problem became "how to make crispy set them properly".
I checked following files:
1. /root/.pyenv/versions/3.7.3/lib/python3.7/site-packages/crispy_forms/bootstrap.py
class Tab(Container):
"""
Tab object. It wraps fields in a div whose default class is "tab-pane" and
takes a name as first argument. Example::
Tab('tab_name', 'form_field_1', 'form_field_2', 'form_field_3')
"""
css_class = 'tab-pane'
link_template = '%s/layout/tab-link.html'
def render_link(self, template_pack=TEMPLATE_PACK, **kwargs):
"""
Render the link for the tab-pane. It must be called after render so css_class is updated
with active if needed.
"""
link_template = self.link_template % template_pack
return render_to_string(link_template, {'link': self})
/root/.pyenv/versions/3.7.3/lib/python3.7/site-packages/crispy_forms/templates/bootstrap3/layout/tab-link.html
<li class="tab-pane{% if 'active' in link.css_class %} active{% endif %}">{{ link.name|capfirst }}{% if tab.errors %}!{% endif %}</li>
I noticed the css_id attr. And I guessed that if one set 'css_id' properly, maybe crispy would do the rest. I tried. And it worked like a charm.
Maybe crispy documentation needs some improvement.
If you- like me- search for answer why this solution doesn't work for you, check if you are using bootstrap 4. To my knowledge tabs from crispy don't work on 5 at least for now.