I have a formset with django-autocomplete-light on the full_name field. I want to be able to TAB into the box and type. Currently, however, if I tab into the box, I can't type, it's like a ChoiceField, but if I click on it (or spacebar) it opens up the dropdown menu and I can type into the area below the menu I clicked on, as though I was typing in the first choice option.
from functools import partial, wraps
from django.urls import reverse
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django import forms
from extra_views import ModelFormSetView, InlineFormSetView, InlineFormSet, CreateWithInlinesView
from dal import autocomplete, forward
from .models import Record, Ledger, Person
from pdb import set_trace as st
# Create your views here.
class NameAutocomplete(autocomplete.Select2ListView):
def get_list(self):
return self.forwarded.get('full_name_choices', [])
def create_form_class(persons):
full_name_choices = [p.full_name for p in persons]
class RecordForm(forms.ModelForm):
full_name = autocomplete.Select2ListChoiceField(
widget=autocomplete.ListSelect2(
url='name_autocomplete',
forward=[
forward.Const(
full_name_choices, 'full_name_choices'
)
]
)
)
class Meta:
model = Record
fields = ['score', 'full_name', ]
return RecordForm
def create_records(request, pk):
ledger_id = pk
ledger = Ledger.objects.get(pk=ledger_id)
persons = Person.objects.filter(ledger_id=ledger_id)
RecordInlineFormSet = forms.inlineformset_factory(
Ledger,
Record,
can_delete=False,
form=create_form_class(persons),
extra=len(persons),
)
if request.method == 'POST':
formset = RecordInlineFormSet(
request.POST,
instance=ledger,
)
if formset.is_valid():
formset.save()
return HttpResponseRedirect('admin')
else:
formset = RecordInlineFormSet(
instance=ledger,
queryset=Person.objects.none(),
)
return render(
request,
'app/ledger_detail.html',
{'formset': formset},
)
Related
I am creating a search application with Django.
I made an article model and a Feedback model that records the rating of articles.
After entering search box and displaying the search results, click one of the results then goes to the detail screen.
After selecting feedback on the detail screen and pressing the submit button, I want to save a search query to the feedback model.
I think that solution is to add a query in the URL like portal/search/?=query and read it, but I don't know how to code it. Also, could you teach me if there is an implementation method other than reading query in the URL?
Also, when I go back from the detail screen, I want to display the previous search results too.
Please comment if you have any questions.
Forgive for my poor English.
models.py
from django.db import models
from django.urls import reverse
from taggit.managers import TaggableManager
class KnowHow(models.Model):
BASIC_TAGS =(
('1','one'),
('2','two'),
('3','three'),
('4','four'),
('5','five'),
('6','six'),
)
CATEGORY =(
('1','Type2'),
('2','Type1'),
)
author = models.ForeignKey('auth.User',on_delete=models.CASCADE)
category = models.CharField(max_length=1,choices=CATEGORY,default='1')
title = models.CharField(max_length=200)
text = models.TextField(blank=True,default=' ')
# delault=' ':import system will give a error if text column is null
file = models.FileField(blank=True,upload_to='explicit_knowhows')
basic_tag = models.CharField(max_length=1,choices=BASIC_TAGS,default='1')
free_tags = TaggableManager(blank=True)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('portal:index')
class Feedback(models.Model):
EFFECT =(
('1','great'),
('2','maybe good'),
('3','bad'),
)
NOVEL =(
('1','I didn't know that'),
('2','I know, but I forgot'),
('3','I know this.'),
)
kh = models.ForeignKey(KnowHow, on_delete=models.PROTECT)
user = models.ForeignKey('auth.User',on_delete=models.CASCADE)
query = models.TextField(blank=True)
time = models.DateTimeField(auto_now_add=True)
efficacy = models.CharField(max_length=1,choices=EFFECT,default='1')
novelty = models.CharField(max_length=1,choices=NOVEL,default='1')
def __str__(self):
return self.time.strftime("%Y/%m/%d %H:%M:%S")
views.py
from django.urls import reverse, reverse_lazy
from django.http import HttpResponse
from django.views import generic
from django.views.generic.edit import ModelFormMixin
from django.shortcuts import redirect,get_object_or_404
from django.core.exceptions import PermissionDenied
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.decorators import login_required
from .models import KnowHow
from taggit.models import Tag
from .forms import SearchForm,FeedbackForm
from django.db.models import Q
"""
Django Auth
The LoginRequired mixin
https://docs.djangoproject.com/en/2.0/topics/auth/default/#the-loginrequired-mixin
The login_required decorator
https://docs.djangoproject.com/en/2.0/topics/auth/default/#the-login-required-decorator
#login_required
"""
class IndexView(LoginRequiredMixin,generic.list.ListView):
model = KnowHow
#paginate_by = 5
ordering = ['-title']
# template_name = 'portal/KnowHow_list.html'
class DetailView(ModelFormMixin,LoginRequiredMixin,generic.detail.DetailView):
# from https://torina.top/detail/337/
model = KnowHow
form_class = FeedbackForm
template_name = 'portal/KnowHow_detail.html'
def form_valid(self, form):
kh_pk = self.kwargs['pk']
Feedback = form.save(commit=False)
Feedback.kh = get_object_or_404(KnowHow, pk=kh_pk)
Feedback.query=""
Feedback.user=self.request.user
Feedback.save()
return redirect('portal:search')
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
self.object = self.get_object()
return self.form_invalid(form)
class CreateView(LoginRequiredMixin, generic.edit.CreateView): # The LoginRequired mixin
model = KnowHow
fields = ['category','title','text','file','basic_tag','free_tags']
#template_name = 'portal/KnowHow_form.html'
def form_valid(self, form):
# This method is called when valid form data has been posted.
# It should return an HttpResponse.
# https://docs.djangoproject.com/en/2.0/topics/class-based-views/generic-editing/#models-and-request-user
form.instance.author = self.request.user
return super(CreateView, self).form_valid(form)
class UpdateView(LoginRequiredMixin, generic.edit.UpdateView): # The LoginRequired mixin
model = KnowHow
fields = ['category','title','text','file','basic_tag','free_tags']
#template_name = 'portal/KnowHow_form.html'
class DeleteView(LoginRequiredMixin, generic.edit.DeleteView): # The LoginRequired mixin
model = KnowHow
success_url = reverse_lazy('portal:index')
def delete(self, request, *args, **kwargs):
result = super().delete(request, *args, **kwargs)
Tag.objects.filter(knowhow=None).delete()
return result
#template_name = 'portal/KnowHow_confirm_delete.html'
class SearchIndexView(LoginRequiredMixin, generic.ListView):
template_name="search/search_index.html"
model = KnowHow
def post(self, request, *args, **kwargs):
form_value = [
self.request.POST.get('basic_tag', None),
self.request.POST.get('free_tags', None),
]
request.session['form_value'] = form_value
self.request.GET = self.request.GET.copy()
self.request.GET.clear()
return self.get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
basic_tag = ''
free_tags = ''
if 'form_value' in self.request.session:
form_value = self.request.session['form_value']
basic_tag = form_value[0]
free_tags = form_value[1]
default_data = {'basic_tag': basic_tag,
'free_tags': free_tags,
}
test_form = SearchForm(initial=default_data)
context['test_form'] = test_form
return context
def get_queryset(self):
if 'form_value' in self.request.session:
form_value = self.request.session['form_value']
basic_tag = form_value[0]
free_tags = form_value[1]
condition_basic_tag = Q()
condition_free_tags = Q()
if len(basic_tag) != 0 and basic_tag[0]:
condition_basic_tag = Q(basic_tag=basic_tag)
if len(free_tags) != 0 and free_tags[0]:
condition_free_tags = Q(free_tags__name__in=free_tags)
return KnowHow.objects.filter(condition_basic_tag & condition_free_tags).distinct()
else:
return KnowHow.objects.none()
#login_required
def help(request):
return HttpResponse("Member Only Help Page")
urls.py
from django.urls import path
from . import views
# set the application namespace
# https://docs.djangoproject.com/en/2.0/intro/tutorial03/
app_name = 'portal'
urlpatterns = [
# ex: /
path('', views.IndexView.as_view(), name='index'),
# ex: /KnowHow/create/
path('KnowHow/create/', views.CreateView.as_view(), name='create'),
# ex: /KnowHow/1/
path('KnowHow/<int:pk>/detail/', views.DetailView.as_view(), name='detail'),
# ex: /KnowHow/1/update/
path('KnowHow/<int:pk>/update/', views.UpdateView.as_view(), name='update'),
# ex: /KnowHow/1/delete
path('KnowHow/<int:pk>/delete/', views.DeleteView.as_view(), name='delete'),
# ex: /KnowHow/help/
path('KnowHow/help/', views.help, name='help'),
path('search/',views.SearchIndexView.as_view(), name='search')
]
There are several solutions for your problem.
First one is the exact solution you mentioned yourself. using a query string parameter like ?q= for KnowHow details view.
Using a SearchLog model and using that model's identifier. When someone hits the /search/ endpoint, you create a new SearchLog and pass the pk for this record to your front. Basically it would be just like ?q= option. instead you can use ?search_id= to bind the feedback to an specific SearchLog
Use user sessions. Bind the searched query to user's session and when they want to create a new Feedback use the query in their session.
For the first two options, you just need to create your urls for the detail links properly (in your search result page). In your template, do something like below:
# You are probably doing something like this
{% for r in results %}
{{r.name}}
{% endfor %}
# You should do this instead
{% for r in results %}
{{r.name}}
{% endfor %}
You can either pass the current_query in your context when rendering the template, or use javascript to get that value from browser's location / query string.
I changed get_context_data function in SearchIndexView to this:
in the last line before return add these two lines
context['basic_tag'] = basic_tag
context['free_tags'] = free_tags
And I changed html too.
{{ KnowHow.title }}
Thanks, #n1ma
I am following a tutorial in which we will create a form to hold simple object parameters.
Here's the code:
forms.py
from django.forms import ModelForm
from .models import Item
class ItemForm(ModelForm):
class Meta:
model = Item
fields = ['item_name', 'item_desc', 'item_price', 'item_image']
models.py
from django.db import models
class Item(models.Model):
def __str__(self):
return self.item_name
item_name = models.CharField(max_length = 200)
item_desc = models.CharField(max_length= 200)
item_price = models.IntegerField()
item_image = models.CharField(max_length=500, default ="https://i.jamesvillas.co.uk/images/jvh/holiday-destinations/resort/food-placeholder.jpg" )
urls.py
from . import views
from django.urls import path
urlpatterns = [
path('', views.index, name = 'index'),
path('item/', views.item, name = 'item'),
path('info/', views.info, name = "info"),
path('<int:item_id>/', views.detail, name = "detail" ),
#add form
path('add', views.create_item, name = "create"),
]
views.py
from django.shortcuts import render, redirect
from django.http import HttpResponse
from .models import Item
from django.template import loader
from .forms import ItemForm
#Some code here
def create_item(request):
form = ItemForm(request.POST or None)
if (form.is_valid()):
form.save()
return redirect('food/index')
return render(request, 'food/item-form.html', {'form': form})
food/item-form.html
<form method = "POST">
{% csrf_token %}
{{ form }}
<button type= "Submit" >Save</button>
</form>
Now when I go to http://localhost:8000/food/add, it displays an empty page! I have followed the tutorial the exact same way then why is My project not working?
correct your views.py
from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import NameForm
def get_name(request):
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = NameForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
# ...
# redirect to a new URL:
return HttpResponseRedirect('/thanks/')
# if a GET (or any other method) we'll create a blank form
else:
form = NameForm()
return render(request, 'name.html', {'form': form})
I'm tryinig to get haystack working with a class-based generic view according to the documentation here. I can get results from a SearchQuerySet in the shell, so the models are being indexed. But I can't get the view to return a result on the page.
The main reason for using the generic view is that I want to extend later with more SQS logic.
I'm probably missing something obvious...
views.py :
from haystack.query import SearchQuerySet
from haystack.generic_views import SearchView
from .forms import ProviderSearchForm
from .models import Provider
class ProviderSearchView(SearchView):
template_name = 'search/provider_search.html'
form_class = ProviderSearchForm
def get_context_data(self, *args, **kwargs):
""" Extends context to include data for services."""
context = super(ProviderSearchView, self).get_context_data(*args, **kwargs)
context['body_attr'] = 'id="provider-search"'
return context
def get_queryset(self):
queryset = super(ProviderSearchView, self).get_queryset()
return queryset.filter(is_active=True)
search_indexes.py:
from haystack import indexes
from .models import Provider
class ProviderIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
title = indexes.CharField(model_attr='name')
created = indexes.DateTimeField(model_attr='created')
def get_model(self):
return Provider
def index_queryset(self, using=None):
"Used when the entire index for model is updated."
return self.get_model().objects.all()
forms.py
from django import forms
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Field, Submit
from crispy_forms.bootstrap import FieldWithButtons
from haystack.forms import SearchForm
from .models import Provider
class ProviderSearchForm(SearchForm):
""" Override the form with crispy styles """
models = [ Provider ]
def __init__(self, *args, **kwargs):
super(ProviderSearchForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.disable_csrf = True
self.helper.form_tag = False
self.helper.form_show_labels = False
self.helper.layout = Layout (
FieldWithButtons(
Field('q', css_class='form-control input-lg', placeholder="Search providers..."),
Submit('','Search', css_class='btn btn-lg btn-primary'))
)
def get_models(self):
return self.models
def search(self):
sqs = super(ProviderSearchForm, self).search().models(*self.get_models())
return sqs
def no_query_found(self):
return self.searchqueryset.all()
The problem was that my page template was using the wrong variable in the for loop.
The documentation suggests:
for result in page_object.object_list
It should be:
for result in page_obj.object_list
note the template variable is page_obj.
See issue post on GitHub
I am making a formset in python/django and need to dynamically add more fields to a formset as a button is clicked. The form I'm working on is for my school asking students who they would like to disclose certain academic information to, and the button here allows them to add more fields for entering family members/people they want to disclose to.
I have the button working to the point where the extra fields show up, and you can add as many as you like. Problem is, the data that was previously entered into the already existing fields gets deleted. However, only the things in the formset get deleted. Everything else that was filled out earlier in the form stays persistent.
Is there any way to make the formset keep the data that was entered before the button was pressed?
form.py:
from django import forms
from models import Form, ParentForm, Contact
from django.core.exceptions import ValidationError
def fff (value):
if value == "":
raise ValidationError(message = 'Must choose a relation', code="a")
# Create your forms here.
class ModelForm(forms.ModelForm):
class Meta:
model = Form
exclude = ('name', 'Relation',)
class Parent(forms.Form):
name = forms.CharField()
CHOICES3 = (
("", '-------'),
("MOM", 'Mother'),
("DAD", 'Father'),
("GRAN", 'Grandparent'),
("BRO", 'Brother'),
("SIS", 'Sister'),
("AUNT", 'Aunt'),
("UNC", 'Uncle'),
("HUSB", 'Husband'),
("FRIE", 'Friend'),
("OTHE", 'Other'),
("STEP", 'Stepparent'),
)
Relation = forms.ChoiceField(required = False, widget = forms.Select, choices = CHOICES3, validators = [fff])
models.py
from django.db import models
from django import forms
from content.validation import *
from django.forms.models import modelformset_factory
class Contact(models.Model):
name = models.CharField(max_length=100)
class Form(models.Model):
CHOICES1 = (
("ACCEPT", 'I agree with the previous statement.'),
)
CHOICES2 = (
("ACADEMIC", 'Academic Records'),
("FINANCIAL", 'Financial Records'),
("BOTH", 'I would like to share both'),
("NEITHER", 'I would like to share neither'),
("OLD", "I would like to keep my old sharing settings"),
)
Please_accept = models.CharField(choices=CHOICES1, max_length=200)
Which_information_would_you_like_to_share = models.CharField(choices=CHOICES2, max_length=2000)
Full_Name_of_Student = models.CharField(max_length=100)
Carthage_ID_Number = models.IntegerField(max_length=7)
I_agree_the_above_information_is_correct_and_valid = models.BooleanField(validators=[validate_boolean])
Date = models.DateField(auto_now_add=True)
name = models.ManyToManyField(Contact, through="ParentForm")
class ParentForm(models.Model):
student_name = models.ForeignKey(Form)
name = models.ForeignKey(Contact)
CHOICES3 = (
("MOM", 'Mother'),
("DAD", 'Father'),
("GRAN", 'Grandparent'),
("BRO", 'Brother'),
("SIS", 'Sister'),
("AUNT", 'Aunt'),
("UNC", 'Uncle'),
("HUSB", 'Husband'),
("FRIE", 'Friend'),
("OTHE", 'Other'),
("STEP", 'Stepparent'),
)
Relation = models.CharField(choices=CHOICES3, max_length=200)
def __unicode__(self):
return 'name: %r, student_name: %r' % (self.name, self.student_name)
and views.py
from django.shortcuts import render
from django.http import HttpResponse
from form import ModelForm, Parent
from models import Form, ParentForm, Contact
from django.http import HttpResponseRedirect
from django.forms.formsets import formset_factory
def create(request):
ParentFormSet = formset_factory(Parent, extra=1)
if request.POST:
Parent_formset = ParentFormSet(request.POST, prefix='Parent_or_Third_Party_Name')
if 'add' in request.POST:
list=[]
for kitties in Parent_formset:
list.append({'Parent_or_Third_Party_Name-0n-ame': kitties.data['Parent_or_Third_Party_Name-0-name'], 'Parent_or_Third_Party_Name-0-Relation': kitties.data['Parent_or_Third_Party_Name-0-Relation']})
Parent_formset = ParentFormSet(prefix='Parent_or_Third_Party_Name', initial= list)
form = ModelForm(request.POST)
if form.is_valid() and Parent_formset.is_valid():
form_instance = form.save()
for f in Parent_formset:
if f.clean():
(obj, created) = ParentForm.objects.get_or_create(name=f.cleaned_data['name'], Relation=f.cleaned_data['Relation'])
return HttpResponseRedirect('http://Google.com')
else:
form = ModelForm()
Parent_formset = ParentFormSet(prefix='Parent_or_Third_Party_Name')
return render(request, 'content/design.html', {'form': form, 'Parent_formset': Parent_formset})
def submitted(request):
return render(request, 'content/design.html')
Thank you in advance!
I've had trouble with dynamically adding fields in Django before and this stackoverflow question helped me:
dynamically add field to a form
To be honest, I'm not entirely sure what you mean by "persistent" in your case - are the values of your forms being removed as you add inputs? Are you sure it isn't something with your JS?
A coworker of mine finally figured it out. Here is the revised views.py:
from django.shortcuts import render
from django.http import HttpResponse
from form import ModelForm, Parent
from models import Form, ParentForm, Contact
from django.http import HttpResponseRedirect
from django.forms.formsets import formset_factory
def create(request):
ParentFormSet = formset_factory(Parent, extra=1)
boolean = False
if request.POST:
Parent_formset = ParentFormSet(request.POST, prefix='Parent_or_Third_Party_Name')
if 'add' in request.POST:
boolean = True
list=[]
for i in range(0,int(Parent_formset.data['Parent_or_Third_Party_Name-TOTAL_FORMS'])):
list.append({'name': Parent_formset.data['Parent_or_Third_Party_Name-%s-name' % (i)], 'Relation': Parent_formset.data['Parent_or_Third_Party_Name-%s-Relation' % (i)]})
Parent_formset = ParentFormSet(prefix='Parent_or_Third_Party_Name', initial= list)
form = ModelForm(request.POST)
if form.is_valid() and Parent_formset.is_valid():
form_instance = form.save()
for f in Parent_formset:
if f.clean():
(contobj, created) = Contact.objects.get_or_create(name=f.cleaned_data['name'])
(obj, created) = ParentForm.objects.get_or_create(student_name=form_instance, name=contobj, Relation=f.cleaned_data['Relation'])
return HttpResponseRedirect('http://Google.com')
else:
form = ModelForm()
Parent_formset = ParentFormSet(prefix='Parent_or_Third_Party_Name')
return render(request, 'content/design.html', {'form': form, 'Parent_formset': Parent_formset, 'boolean':boolean})
def submitted(request):
return render(request, 'content/design.html')
Thank you for your input, those of you who answered :)
I was once trying to do something like this, and was directed to django-crispy-forms by a man much wiser than I. I never finished the project so I can't offer more help than that, but it could be a starting point.
If your formset does not show the input you made before that means it does not see model's queryset. Add queryset to formset arguments to resolve this. For example:
formset = SomeModelFormset(queryset=SomeModel.objects.filter(arg_x=x))
i am trying to make a chained select menu, i have this model:
from django.db import models
class Health_plan(models.Model):
name = models.CharField(max_length=15)
class Doctors_list(models.Model):
name = models.CharField(max_length=30)
specialty = models.CharField(max_length=15)
health_plans = models.ManyToManyField(Health_plan, related_name="doctors")
location = models.CharField(max_length=15)
def __unicode__(self):
return self.name
And this is my forms.py:
class SpecForm(ModelForm):
a = Doctors_list.objects.values_list('specialty', flat=True)
unique = [('---------------','---------------')] + [(i,i) for i in set(a)]
specialty = forms.ChoiceField(choices=unique)
class Meta:
model = Doctors_list
class HealthForm(ModelForm):
hplan = ChainedForeignKey(
Health_plan,
chained_field="specialty",
chained_model_field="specialty",
show_all=False,
auto_choose=True
)
my urls.py:
from django.conf.urls import patterns, include, url
from testApp.views import spec_form
from testApp.views import health_form
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
# Examples:
url(r'^$', 'Medbook.views.home', name='home'),
# url(r'^Medbook/', include('Medbook.foo.urls')),
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
url(r'^admin/', include(admin.site.urls)),
url(r'^hello/$', spec_form),
url(r'^hello/$', health_form),
)
and my views.py:
from django.shortcuts import render_to_response
from testApp.forms import SpecForm
from testApp.forms import HealthForm
def spec_form (request):
if request.method == 'POST':
form = SpecForm(request.POST)
if form.is_valid():
form.save()
else:
form = SpecForm()
return render_to_response('hello.html', {'form':form})
def health_form (request):
if request.method == 'POST':
form = HealthForm(request.POST)
if form.is_valid():
form.save()
else:
form = SpecForm()
return render_to_response('hello.html', {'form':form})
I am new to Django and i find this tricky. The user must select one specialty, and then should appear the health_plans that cover that specialty.
The health_plans have a manytomany relationship with the doctors. When a person chooses a specialty, the script should check wich doctors belong to that specialty and retrieve all the health plans hold by those doctors.
So far the only thing i get in the menu is: Health_plan object
Health_plan object
Health_plan object
I hope i made it clear, for my code it isn't.
Any help kindly appreciated
This has nothing to do with chained selects, and most of the code here is irrelevant. The issue is that, while Doctors_list has a __unicode__ method, Health_plan does not. Define one for that model too.
(Also note that the usual style for model names is CapWords: DoctorsList and HealthPlan. Although the former actually only refers to a single doctor, so it should just be Doctor.)