I have three mysql tables without pk’s assigned, table A contains unique names, B contains distinct names and value 1 (longer than table 1), and table C is distinct values of names, value 1 and value 2 (longer than table B). I am struggling to define models and form for these relationships to create a form (Django form or html select form) to do what django smart_selects promises to do. https://github.com/digi604/django-smart-selects, I have followed the documentation and many answers on the net. But my app is still not working. here is my code:
there may be typo's or spacing issues on this post, but those are not the cause of errors in my app.
I am using python 2.7, django 1, 10, 0
smart_selects is added to settings, project url (not app url), etc.
models.py
class Names(models.Model):
name = models.CharField(max_length=150, unique=True)
class Meta:
db_table = 'A'
class Foo(models.Model):
Name = models.ForeignKey(Names, to_field = 'name', db_column='name')# need to assign db_column or get error Name_id in fieldlist, it is possible that “to_field” is not needed here. But I am not sure
Val1 = models.BigIntegerField(blank=True, null=True)
class Meta:
db_table = 'B'
class Foo2(models.Model):
name = models.ForeignKey(Names, db_column='name', to_field="name", related_name = 'name_')
VAL1 = ChainedForeignKey(Foo, chained_field="name", chained_model_field="Name", db_column='val1')#again, need to define db_column name or get error VAL1_id not in field list
class Meta:
db_table = 'C'
forms.py -not currently using this form, please see view and html.
class SearchForm(ModelForm):
class Meta:
model = Foo2
fields = '__all__'
#The form left like is will render empty select boxes. I have to define
def __init__(self, *args, **kwargs):
super(SearchForm, self).__init__(*args, **kwargs)
self.fields['name'] = forms.ModelChoiceField(Foo2.objects.all().values_list('name', flat=True))
#other form lines removed.
and view.py
def index(request, name):
value = Foo2.objects.all().values('names', 'VAL1')
return render(request, 'index.html', {'form':value})
html
<form action="" method="POST">{% csrf_token %}
<h3>Filters</h3>
{% if form %}
<select>
{% for co in form %}
<option value="{{ co.names }}">{{ co.names }}</option>
{% endfor %}
</select>
<br>
<select>
{% for co in form %}
<option value="{{ co.VAL1 }}">{{ co.VAL1 }}</option>
{% endfor %}
</select>
{% endif %}
<br>
<input type="submit" value="Submit" class="submit" name="submit">
</form>
After writing the code, I have run:
Python manage.py makemigrations app_name
Python manage.py migrate --fake app_name
The application works, but the VAL1 select box is not filtered (smart_select functionality is not working). What I need to know if there is something wrong in my design of the models/apps.
thank you
I have the same problem. Apparently, smart_selects cannot handle ForeignKeys with 'to_field'. Also, try accessing the admin page and see if the dropdown menus work there. What I did was to do the tutorial exactly in a test app, see if smart_selects works for the test models, and then plug in my models to see if they would.
In my case, the test models worked but my own models didn't -- but removing the 'to_field' made it work. I'll be reading the source code and see if I can find a workaround.
Related
I tried multiple solutions around but couldn't make my select field work in Django Template. I am beginner and humbly apologize if my question is not up to standard. My select button is not showing any options in django form. I only have two options to show. Is there anyway I can get form field without having iterate through it in template? Using Django 3 and Python 3.8. Help will be much appreciated.
Model:
class User(AbstracUser):
ACC_TYPE = (('Student', 'Student'), ('Teacher', 'Teacher'))
role = models.CharField(max_length=100, choices=ACC_TYPE
.....
Form:
class UserForm(UserCreationForm): #Using django's default form
account_type = forms.CharField(widget=forms.Select(choices=User.ACC_TYPE))
model = User
fields =('.............,
'account_type')
def __init__(self, *args, **kwargs):
super(UserCreationForm, self).__init__(*args, **kwargs)
self.fields['account_type'].choices = [(each[0], each[1]) for each in User.ACC_TYPE]
self.fields['account_type'].required = True
Template:
.......
<select>
{% for opt, val in form.account_type.choices %}
<option value="{{ opt }}">{{ value }}</option>
{% endfor %}
</select>
......
Your logic is fine. You shouldn't be having this issue. Only reason could be and its a long shot...that you are using some bootstrap theme with customized CSS classes in it. Now either you load those custom classes per field or you can use django-crispy-fields that inherits all classes with form-group. You will need to load the form with {{ form|crispy }}. For details please check settings for django-crispy-forms. Remember to load the tag at top of page where you want to use it as per settings {% load crispy_forms_tags %}. Read Docs here.
I am getting this error while I was trying to make two forms (with two models) and process that in the view. My SongForm is not saving its data in database while AlbumForm is perfectly saving its data.
views.py-
def formm(request):
if request.method=='POST':
songform = SongForm(request.POST)
albumform=AlbumForm(request.POST)
if songform.is_valid() and albumform.is_valid():
songform.save()
albumform=albumform.save(commit=False)
albumform.date=timezone.now()
albumform.save()
return redirect("result")
forms.py-
from django import forms
from . models import Album,Song
class SongForm(forms.ModelForm):
class Meta:
model=Song
fields=('song_title','genre')
class AlbumForm(forms.ModelForm):
class Meta:
model=Album
fields=('album_title',)
models.py-
from __future__ import unicode_literals
from django.db import models
class Album(models.Model):
album_title=models.CharField(max_length=50)
date=models.DateTimeField(blank=True,null=True)
def __str__(self):
return self.album_title
class Song(models.Model):
song_title=models.CharField(max_length=50)
genre=models.CharField(max_length=50)
album=models.ForeignKey(Album,on_delete=models.CASCADE)
def __str__(self):
return self.song_title
formmpage.html-
{% extends "musicapp/basepage.html" %}
{% block content %}
<form method="POST" class="post-form">
{% csrf_token %}
{{ songform.as_p }}
{{ albumform.as_p }}
<button type="submit" class="btn btn-info">POST</button>
</form>
{% endblock %}
Do correct me, where actually I am doing wrong. I guess it is in my views.py.
It looks as if you should be setting the song's album before you save it.
if songform.is_valid() and albumform.is_valid():
album = albumform.save(commit=False)
album.date = timezone.now()
album.save()
song = songform.save(commit=False)
song.album = album
song.save()
return redirect("result")
Note that I changed the code to album=albumform.save(commit=False), since the save() method returns a model instance, not a form instance.
Ok,I was certain this was the issue but i've used the code you've given me and I get an integrity error due to the fact that your Song model has a Foreign Key to the Album model without null=True it won't allow you to create a new Song without an Album model.
There has to be some kind of business logic to know whether a song belongs to an album. In my opinon you should have these 2 in separate endpoints.
When creating a Song you should be able to select from a list albums that it may or may not belong to based on a genre.
Anyways, you can get past this with just adding null=True on the the album Foreign key attribute in your Song model.
I'm trying to make a search function that allows me to enter the value of an object in an instance so I can display that instance and several of it's objects on the same page as the search page. Here's what I have so far:
#models.py
class Student(models.Model):
# STEP 1 BASIC INFO
student_id = models.CharField(max_length=128, unique=True)
first_name = models.CharField(max_length=128)
last_name = models.CharField(max_length=128)
ssn = USSocialSecurityNumberField(null=False)
gender = models.CharField(max_length=128, choices=GENDER_CHOICES)
dob = models.DateField(auto_now=False, auto_now_add=False, db_column="date of birth")
contact_number = models.CharField(max_length=128)
address = models.CharField(max_length=128)
city = models.CharField(max_length=128)
state = USStateField(choices=STATE_CHOICES, default='NJ')
zipcode = USZipCodeField(blank=True)
country = CountryField(default='US', blank=True)
home_phone = models.CharField(max_length=128)
cell_phone = models.CharField(max_length=128)
email = models.EmailField(max_length=254, validators=[validate_email])
def __str__(self):
return self.first_name + self.last_name
#views.py
def search_Student(request):
context_dict = {}
if request.method == 'POST':
query = request.POST['last_name_search']
results = Student.objects.filter(last_name=query)
if query:
context_dict['results'] = results
else:
context_dict['no_results'] = query
return render(request, "students/search_student.html", context_dict)
#search_student.html
{% block main_content %}
<form method="post" action="/students/search_student/">
{% csrf_token %}
<label for="last_name_search">Last Name:</label>
<input type="text" name="last_name_search" id="last_name_search">
<input type="submit" name="submit">
</form>
<div id="result_panel">
{% if no_results %}
No results returned for <q>{{ no_results }}</q>
{% else %}
{% for result in results %}
{{ result.last_name }}
{% endfor %}
{% endif %}
</div>
{% endblock %}
#urls.py
urlpatterns = [
url(r'^$', students_views.Students, name='students'),
url(r'^add_student/$', students_views.add_Student, name='add_student'),
url(r'^id=(?P<identify>[\w]+)/add_studentcourse/$', students_views.add_StudentCourse, name='add_studentcourse'),
url(r'^id=(?P<identify>[\w]+)/add_studentemployment/$', students_views.add_StudentEmployment, name='add_studentemployment'),
url(r'test/$', students_views.test, name='test'),
#URL for the search page.
url(r'^search_student/$', students_views.search_Student, name='search_student'),
url(r'^current_student/$', students_views.current_Student, name='current_student'),
url(r'^all_my_student/$', students_views.all_My_Student, name='all_my_student'),
url(r'^public_student/$', students_views.public_Student, name='public_student'),
url(r'^sales_reports/$', students_views.sales_Reports, name='sales_reports'),
url(r'^switch_counselor/$', students_views.switch_Counselor, name='switch_counselor'),
url(r'^source_admin/$', students_views.source_Admin, name='source_admin'),
url(r'^super_tool/$', students_views.super_Tool, name='super_tool'),
url(r'^help_and_settings/$', students_views.help_And_Settings, name='help_and_settings'),
]
Basically, if I type in a last name in the input, I want it to be able to grab all model instances that have a last name equal to that, and to be able to put any information I want about all the matching instances, like the name, gender, and address on the exact same page as the search. The POST method may be confusing, but someone insists that I do this (It didn't work when I changed the method to GET either). Could someone please point out any errors or missing pieces in my code? Thanks.
Edit: Added the urls.py.
How to debug this
Remove the action from the tag. The browser will automatically post to the same URL. This rules out any issues with redirects, mappings or upstream urlconfs.
Use View Source in the browser to make sure nothing is returned, as opposed to the div being hidden by CSS or JS.
Make sure results are found:
views.py
if query:
results = Student.objects.filter(last_name=query)
if results.count():
context_dict['results'] = results
else:
context_dict['no_results'] = query
template
<div id="result_panel">
{% if no_results %}
No results returned for <q>{{ no_results }}</q>
{% else %}
{% for result in results %}
{{ result.last_name }}
{% endfor %}
{% endif %}
</div>
Note that the above cannot ever be empty. It should always show something. If it does not, it is a display error and the data is there. See point 2.
If it shows the "no results" part, then try to replicate the query in the django shell:
python manage.py shell
>>> from yourapp.models import Student
>>> Student.objects.filter(last_name='what you typed')
>>> Student.objects.filter(last_name__iexact='what you typed')
>>> Student.objects.filter(last_name__icontains='what you typed')
>>> Student.objects.count()
If none of the first three returns results, then you made a typo or number 4 will show you have no students.
SO won't let me comment quite yet, so here to say that you can indeed (and are) supposed to treat dicts like a list in django templating seen here.
Can you please post your error? I'm not sure I'm seeing whats wrong. Your method adapts over to my code just fine, so I'm a bit lost.
Editing for formatting:
You should maybe make a compile time dictionary, IE:
context_dict = {
"results": results
}
return render(request, "students/search_student.html", context_dict)
Make sure to return in the if, so the scope is in the proper place, and if your if finds nothing return a variable saying so.
If you're bent on sticking to your answer, trying referencing it in the template as so..
{% for result in results.results %}
Your dictionary has a results entry, that points to the variable results. I believe you are iterating through the dictionary in your current example, as opposed to all the queries.
Of course you can use a view method and try to find the error in your custom search and template code.
OR you can do it in a more Django way, relying on pre-built features of Django:
Use Django's ListView to query on and display the Student Model objects. You get paging, error handling, context setup for free with this. See other StackOverflow questions like https://stackoverflow.com/a/33350839/621690 for example code.
Your code that filters the Students would go into get_queryset - or you can use django-filter.
For nice usability, you can add Select2 to your form input which allows autocomplete/lookahead. See django-select2
So I am working on a small Django project, which for the moment doesn't require optimization. But to prepare for the future, I'd like to know a bit more about the three approaches.
For instances, as part of the models, I have User and UserProfile, Transaction.
class User(models.Model):
name = ...
email = ...
class UserProfile(models.Model):
user = models.ForeignKey(User, related_name='profile')
photo = models.URLField(...)
...
class Transaction(models.Model):
giver = models.ForeignKey(User, related_name="transactions_as_giver")
receiver = models.ForeignKey(User, related_name='transactions_as_receiver')
...
I frequently need to do something like "return transactions that the request.user is giver or receiver", or "return the profile photo of a user". I have several choices, for instance to get a list of pending transactions and photos of both parties, I can do it at views.py level:
1.
#views.py
transactions = Transaction.objects.filter(Q(giver=request.user)|Q(receiver=request.user))
for transaction in transactions:
giver_photo = transactions.giver.profile.all()[0].photo
# or first query UserProfile by
# giver_profile = UserProfile.objects.get(user=transaction.giver),
# then giver_photo = giver_profile.photo
#
# Then same thing for receiver_photo
transaction['giver_photo'] = giver_photo
...
Or I can do it more on template level:
# some template
<!-- First receive transactions from views.py without photo data -->
{% for t in transactions %}
{{t.giver.profile.all.0.photo}}, ...
{% endfor %}
Or I can move some or even all of the above stuffs into filters.py
# some template
{{ for t in request.user|pending_transactions }}
{{ t.giver|photo }} {{ t.receiver|photo }}
{{ endfor }}
where photo and pending_transactions are roughly the same code in original views.py but moved to a filter.
So I wonder is there a best practice/guide line on how to choose which approach?
From Django documentation, lower level is faster, and therefore 2. 3. should be slower than 1; but how about comparing the 2. and 3.?
In getting a user photo, which of the two should be recommended, transactions.giver.profile.all()[0].photo OR profile = UserProfile.objects.get(...) --> photo = profile.photo?
Move this logic into models and managers. Views and templates must be as short as possible.
class User(models.Model):
...
def transactions(self):
return Transaction.objects.filter(Q(giver=self)|Q(receiver=self))
def photo(self):
return self.profile.all().first().photo
So the template will be:
{% for t in request.user.transactions %}
{{ t.giver.photo }} {{ t.receiver.photo }}
{% endfor %}
My experience says that business logic in model as much easier to test, support and reuse than in the views/templates.
I'm trying to let the user select one 'thing' from a list (from the database), then go find other stuff in the database using that record. But I cannot get the selection info from the selection page.
I'll try to make this a pretty complete snapshot of the relevant code, but I may remove too much or leave too much in, sorry.
my models.py:
urlpatterns = patterns('',
url(r'^$', 'dblook.views.index', name='home'),
url(r'^dblook3/', 'dblook.views.try3', name='home2'),
url(r'^dblook4/', 'dblook.views.try4', name='home3'),
)
my dblook/models.py:
from django.db import models
class serial_number(models.Model):
def __unicode__(self):
return self.serialno
#return self.question
class Meta:
managed=False
db_table='serial_number'
sn_id = models.AutoField(primary_key=True)
serialno = models.CharField(max_length=128)
comment = models.ForeignKey(comment,null=True,db_column='comment')
my views.py (I will skip all the imports other than the database model import. If anyone really wants them I'll update with them)
from dblook.models import *
class SerialnoSelectForm(forms.Form):
serialno = forms.CharField(max_length=16)
selected = forms.BooleanField()
class serialform(ModelForm):
class Meta:
model = serial_number
exclude=('comment','sn_id')
selected = forms.BooleanField()
class snselect(forms.Form):
sno = forms.ChoiceField()
def try3(request):
if ( request.POST ):
output = "HEllo world, thanks for posting"
return HttpResponse(output)
else:
sslst = snselect(serial_number.objects.filter(serialno__startswith="A128").order_by('-serialno'))
t = loader.get_template('select_serialno.html')
c = Context({
'sslst': sslst,
})
c.update(csrf(request))
return HttpResponse(t.render(c))
def try4(request,dsn):
if ( request.POST ):
output = "HEllo world, thanks for posting to 4"
return HttpResponse(output)
else:
return HttpResponse("Error")
And my template (select_serialno.html) is:
<h1>Select a serial number</h1>
<ul>
<form method='post' action'='/dbtest4/{{serial_number.sn_id}}/showme'>
{% csrf_token %}
{% for sn in sslst %}
<input type="submit" name="sn.serialno" id="choice{{ forloop.counter }}" value="{{choice.id}}"/>
<label for="choice{{ forloop.counter }}">{{ sn.serialno }}</label><br/>
{% endfor %}
<input type="submit" value="data" />
</form>
When I go to dblook3, I get a nice list from the database of serial numbers, along with a button that, if I hit goes immediately to the dblook4 URL (in this case, its ALWAYS '/dbtest4//showme/' instead of something like '/dbtest4/3/showme/). Unfortunately, I cannot seem to have any way to tell what button they hit.
No matter WHAT I put in for the 'stuff' in <form method='post' action'='/dbtest/{{stuff}}/showme'>, it is always empty.
I also tried things like if( 'choice' in request.POST ): in try4 in veiws.py, but that didn't work either.
So, how do I get ANY information about what was selected from 'look3' over to 'look4'? I'll take just about anything... However, if you can explain why I'm doing that hopefully your answer will not only solve my problem, but help others understand...
(if the above looks pretty 'evolutionary' that's because I've been hacking on this for 3 days now...)
Thanks!
You need to POST the information to the look4 dblook form:
<form method='post' action'='{% url dblook.views.try4 %}'>
At the moment you have /dbtest/{{serial_number.sn_id}}/showme which doesn't make any sense. You don't have a serial_number variable in your context so I don't know where that comes from. You have def try4(request,dsn): as your view definition which suggests that you are trying to load information on the try4 view depending on what was selected fromt he try3 view (although I am guessing this as you haven't explained what you are trying to do). If that is the case, you need to do that based on the data passed via POST instead of url parameters. Something very vaguely like the following:
def try4(request):
if request.method == "POST":
form = snselect(request.POST)
if form.is_valid():
data = form.cleaned_data
# Get the selected item from your choice field and retrieve the
# corresonding model object with that id
...