how to query between multi tables in django - python

See some snippets please:
1.Model UserProfile:
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
email = models.EmailField(unique=True)
HEAD_CHOICE = (
('1', 'M'),
('2', 'F'),
)
image_id = models.CharField(max_length=2, choices=HEAD_CHOICE, default='2')
2.Model TimeLine:
from django.db import models
from django.contrib.auth.models import UserProfile
class TimeLine(models.Model):
user = models.ForeignKey(UserProfile)
3.TimeLine's views.py
from models import TimeLine
from django.shortcuts import render_to_response
def index(request):
timelinedict = TimeLine.objects.all()
return render_to_response('timeline.html', locals())
Question: how can I make the var 'timelinedict' contain fields (image_id, email) of UserProfile.
Thanks in advance:)

Question: how can I make the var 'timelinedict' contain fields
(image_id, email) of UserProfile.
It already does:
from models import TimeLine
from django.shortcuts import render
def index(request):
timelinedict = TimeLine.objects.all()
return render(request, 'timeline.html', {'objects': timelinedict})
In timeline.html:
{% for obj in objects %}
{{ obj.user.email }}
{{ obj.user.image_id }}
{% endfor %}
Use the render shortcut, not render_to_response. render will return the correct request context, which is useful when you are processing forms. It is best to get into the habit of using render.
Don't use locals(); because you will send every variable in the scope to your template. This is never what you want. Explicit is better than implicit.

You don't need to do anything special, you can directly access those attributes from instance of TimeLine.
For example
for t in TimeLine.objects.all():
print t.user.image_id, t.user.email
Similarly you can use that in template as well.

Your example for timelinedict isn't actually a dict it's a queryset containing TimeLine objects.
I think using the #property decorator as below will allow you to attach attributes to your TimeLine model objects.
class TimeLine(models.Model):
user = models.ForeignKey(UserProfile)
#property
def image_id(self):
return self.user.image_id
#property
def email(self):
return self.user.email
Of course, you could just access them directly in your template via object.user.image_id, etc.

Related

How to use autocompleteselect widget in a modelform

I know there is a new feature in Django 2.0, which is AutocompleteSelect widget in ModelAdmin.
I am trying to use it in my custom modelForm but just failed.
Tried like this
#unit is the foreign key to the incident
class AccountForm(forms.ModelForm):
class Meta:
model = Invoice
...
...
widgets = { 'incident':widgets.AutocompleteSelect(Invoice._meta.get_field('incident').remote_field, admin.site)
}
...
#Invoice model
class Invoice(models.Model):
...
incident = models.ForeignKey(Unit, on_delete=models.CASCADE,null=True)
...
Hope anyone can help me.
Thanks
The AutocompleteSelect widget will not work outside of the admin site.
If you are using AccountForm in admin site you can use the following code:
class AccountForm(forms.ModelForm):
...
incident = forms.ModelChoiceField(
queryset= Unit.objects.all(),
widget=AutocompleteSelect(Invoice.incident.field.remote_field, admin.site),
)
...
class Meta:
model = Invoice
fields = [
'incident',
...
]
#admin.register(Invoice)
class InvoiceAdmin(admin.ModelAdmin):
form = AccountForm
AutocompleteSelect has 2 required args, rel and admin_site. The rel is used to extract the model used to query the data from and relates to an attribute on a ForeignKey or ManyToManyField. Since I wanted to use this on a field that wasn't actually a ForeignKey, I needed to override a few things to make it work:
class ClientAutocompleteSelect(AutocompleteSelect):
def get_url(self):
model = Client
return reverse(self.url_name % (self.admin_site.name, model._meta.app_label, model._meta.model_name))
class ClientChoiceField(forms.ModelChoiceField):
def __init__(self, queryset=None, widget=None, **kwargs):
if queryset is None:
queryset = Client.objects.all()
if widget is None:
widget = ClientAutocompleteSelect(None, admin.site) # pass `None` for `rel`
super().__init__(queryset, widget=widget, **kwargs)
def to_python(self, value):
return value # just return the client_id and not a Client object
class MyAdminForm(forms.ModelForm):
client_id=ClientChoiceField()
...
This requires that the end user has admin read access to the autocomplete endpoint of the model being queried. You may be able to do more hacking to change that get_url and use your own endpoint to give search results, though.
I spent a few hours trying to understand why my code (built on #Tim 's answer) would not work, until I stumble on a comment about missing references to css/js files.
Here is a complete working solution to use the AutocompleteSelect widget in any custom form for signed-in users having both 'staff' and 'view' access to the given model:
from django.urls import reverse
from django.contrib.admin.widgets import AutocompleteSelect
from django.contrib import admin
class UserAutocompleteSelect(AutocompleteSelect):
def get_url(self):
model = CustomUser
return reverse(self.url_name % (self.admin_site.name, model._meta.app_label, model._meta.model_name))
class UserChoiceField(forms.ModelChoiceField):
def __init__(self, queryset=None, widget=None, **kwargs):
if queryset is None:
queryset = CustomUser.objects.all()
if widget is None:
widget = UserAutocompleteSelect(None, admin.site) # pass `None` for `rel`
super().__init__(queryset, widget=widget, **kwargs)
class UserAutocompleteSelectForm(forms.ModelForm):
"""
for changing user on Play objects
using amdin module autocomplete
"""
user = UserChoiceField(
# queryset=CustomUser.objects.all(),
help_text=_('Select the user to replace the current one')
)
class Meta:
model = Play
fields = ('user', )
You can use the same, replacing CustomUser and Play by your own models
If (like me) this is not working out-of-the-box with the html template you're using, that means that you need to include the required css/js files to your template. Here is a simple way to do it :
Providing that the form is declared as such in the view:
form = UserAutocompleteSelectForm()
...
context = {
'form': form,
...
}
return render(request, 'users/change_user.html', context)
you should add the following lines to the html template to include the required css/js files:
{% block extrahead %}
{{ block.super }}
{{ form.media }}
{% endblock %}

Django ModelForm with model having ForeignKey field does not display selectBox correctly

This is probably easy to solve. I created a form which use forms.ModelForm.
My model has ForeignKey field. Form creates a select field for foreignKey, but but does not display value correctly.
models.py
from django.db import models
# Create your models here.
class Gender(models.Model):
name = models.CharField(max_length=8, null=True)
class Meta:
db_table='gender'
class UserExample(models.Model):
name = models.CharField(max_length=16,null=True)
age = models.IntegerField(blank=True, null=True)
gender = models.ForeignKey('Gender', on_delete=models.SET_NULL, null=True)
class Meta:
db_table='userExample'
def __str__(self):
return ""
forms.py
from django import forms
from .models import UserExample, Gender
class UserForm(forms.ModelForm):
class Meta:
model = UserExample
fields = ('name', 'age', 'gender')
views.py
from django.shortcuts import render
from .forms import UserForm
# Create your views here.
def index(request):
form = UserForm
return render(
request,
'index.html',
{'form': form}
)
urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.index, name='index'),
]
index.html
<html>
<head>
<title>test </title>
</head>
<body>
<form action="formreturn/" method="post">
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
</body>
</html>
And after I launch my app. In select box I get only selection for gender objects but not for gender names.
Optional I used to add values using sqllite3 like this:
sqlite> insert into gender values(1,"Male");
sqlite> insert into gender values(2,"Female");
Implement __unicode__ or __str__ method in Gender model,
def __unicode__(self):
return '%s' % self.name
And it will display gender names in your option choices.
How to customize the default form field behavior of ForeignKey Model field
ForeignKey maps to ModelChoiceField and you can override the default behavior of the same. You can override the 'Select' option field value using 'to_field_name' parameter (useful when you have multiple unique fields in your related model) otherwise option field values are defaulted to pk field.
'empty_label' will change the default "--------" with the empty_label atrribute
forms.py
class UserForm(forms.ModelForm):
gender = forms.ModelChoiceField(queryset=Gender.objects.all(),
to_field_name = 'name',
empty_label="Select Gender")
class Meta:
model = UserExample
fields = ('name', 'age', 'gender')
Option display names are defaulted to __str__ method output. You can override the default behavior by writing a custom choice field class (inherited from ModelChoiceField) and override the label_from_instance() method.
def__str__(self):
return self.name
You will add this on your gender class
I already gave this answer in another place, hope it's not bad to copy paste.
In my case, I didn't wanna go make an str for my billion models, so I just did this:
You can make one custom ModelChoiceField to which you can pass a function. That way if you have different fields for which you want different attributes to be displayed, you can have only 1 class:
class CustomModelChoiceField(forms.ModelChoiceField):
name_function = staticmethod(lambda obj: obj)
def __init__(self, name_function, *args, **kwargs):
if not name_function is None: self.name_function = name_function
super(CustomModelChoiceField, self).__init__(*args, **kwargs)
def label_from_instance(self, obj):
return self.name_function(obj);
You can then call it as simply as this:
form_field = CustomModelChoiceField(
lambda obj: obj.get_full_name(),
queryset=Whatever.objects.all(),
)
You can also pass None in case you're doing some dynamic stuff and it'll just basically default to a regular ModelChoiceField. I'm not too much of a python guy but this works for me.

Variables (fields?) Won't Display in Django Generic Detail View

I have been loosely following a tutorial and can't seam to get a generic detail view to work properly. I am calling with a pk and the page displays but the variable {{publisher.name}} doesn't show up. I have deleted some of the code from views and the model which I consider peripheral but if there error isnt obvious I can repost.
All files are in the poll directory except the HTML file is in poll/template/poll
Thanks
The URL.py is
from django.conf.urls import url
from poll.views import PublisherList
from . import views
app_name = "poll"
urlpatterns = [
url(r'^publishers/$', PublisherList.as_view(), name = "publisherlist"),
url(r'^start/', views.PublisherCreate.as_view(), name = 'make-publisher'),
url(r'^(?P<pk>[0-9]+)/$', views.PublisherDetail.as_view(), name = 'detail-publisher'),
]
The View.py
from django.shortcuts import render
from django.views.generic.edit import CreateView
from django.views import generic
from django.views.generic import ListView
from poll.models import Publisher
...
class PublisherDetail(generic.DetailView):
model = Publisher
template_name = 'Poll/publisher_details.html'
and the HTML file
{% extends "personal/header.html" %}
{% block content %}
<h1>{{ Publisher.name }}</h1>
<h1>Options</h1>
{%endblock%}
and the models.py
from django.db import models
from django.core.urlresolvers import reverse
# Create your models here.
class Publisher(models.Model):
name = models.CharField(max_length=30)
address = models.CharField(max_length=50)
city = models.CharField(max_length=60)
state_province = models.CharField(max_length=30)
country = models.CharField(max_length=50)
website = models.URLField()
class Meta:
ordering = ["-name"]
def __str__(self): # __unicode__ on Python 2
return self.name
def get_absolute_url(self):
return reverse('build:details', kwargs = {'pk':self.pk})
The object in the template is called publisher, not Publisher.
You can't access the instance of the model like this in the template. Publisher is the name of your Model class but not an instance of it. The default name of the object in a generic detail view is object. So you need to use {{ object.name }}. Or you can use the lowercased Model name too as a default. In your case thats publisher.
If you want to change the variable name of your object you have to implement get_context_object_name(obj) method of your detail view.
The method has to return a string with the desired variable name of the object in your detail view template.
The default name of the object in the template is object, while the lowercase model name serves as an alias (publisher). You can specify that name in the view via the class attribute context_object_name, as described in the docs:
class PublisherDetail(generic.DetailView):
# ...
context_object_name = 'foo'
Then
{{ foo.name }}
# {{ publisher.name }} should work out of the box
will work in the template.

Django Model Form: show query form and handle post request for OneToMany fields

I am a newbie to Django and could not find similar questions after searching on google/SO.
I've a model named Questions, which has multiple(2-4) choices and defined as below:
class Question(models.Model):
name = models.CharField(max_length=128)
class Choice(models.Model):
name = models.CharField(max_length=256)
is_correct = models.BooleanField(default=False)
question = models.ForeignKey(Question, on_delete=models.CASCADE)
Of the multiple choices only one is correct.
What I want to do: In just one page, user could submit a question together with multiple choices, here is a draft of UI:
My first question: I've defined ModelForm but don't know how to add "choices" field to QuestionForm:
class QuestionForm(ModelForm):
name = forms.CharField(max_length=128)
description = forms.CharField(max_length=256)
class Meta:
model = Question
fields = ['name', 'description']
class ChoiceForm(ModelForm):
name = forms.CharField(max_length=256)
is_correct = forms.BooleanField()
class Meta:
model = Choice
fields = ['name', 'is_correct']
Is it possible to use ModelForm the render the above HTML page besides writing it manually?
My second question: If use clicks "Submit" button, I use AJAX to send json data to backend server, here is an example of form data:
name:question1
choices[][name]:choice1
choices[][is_correct]:1
choices[][name]:choice2
choices[][is_correct]:0
And this is my code handling the request:
form = QuestionForm(request.POST)
if form.is_valid():
question = form.save()
How to parse choices from the request?
How could I parse data of multiple choices part from the POST request?
Again, I'm a newbie to Django and any answers/suggestions is highly appreciated.
To create forms for models which have a OneToMany relation I would recommend you to use Django's inline formsets: https://docs.djangoproject.com/en/1.8/topics/forms/modelforms/#inline-formsets
It's a really simple and elegant way to create forms for related models.
To parse the choices, the user entered you could just override the clean method of your form. In this the user content is usually checked and prepared for storing it to the database. https://docs.djangoproject.com/en/1.8/ref/forms/validation/#form-field-default-cleaning
So cleaning could look like this:
class QuestionForm(ModelForm):
...
def clean(self):
cleaned_data = super(QuestionForm, self).clean()
if cleaned_data['choice_name'].startswith('Something'):
raise forms.ValidationError(
"Choice names cannot start with 'Something'!"
)
You models seems to be correct, in order to be able to add mutiple choices in your template you need a formset. In addition you can put a formset and a form inside the same html form in a template and have them be validated individually. Each one only cares about the POST data relevant to them. Something like:
template.html
<form method="post" action="">
{% csrf_token %}
{{ choices_formset.management_form }} <!-- used by django to manage formsets -->
{{ question_form.as_p }}
{% for form in choices_formset %}
{{ form.as_p }}
{% endfor %}
<button type='submit'>Submit</button>
</form>
views.py
from django.db import IntegrityError, transaction
from django.shortcuts import redirect
from django.forms.formsets import formset_factory
from django.core.urlresolvers import reverse
def new_question(request):
ChoicesFormset = formset_factory(ChoicesForm)
if request.method == 'POST':
question_form = QuestionForm(request.POST)
choices_formset = ChoicesFormset(request.POST)
if question_form.is_valid():
question = Question(**question_form.cleaned_data)
if choices_formset.is_valid():
question.save()
new_choice_list = list()
append_choice = new_choice_list.append
for form in choices_formset:
form.cleaned_data.update({'question': question})
append_choice(Choice(**form.cleaned_data))
try:
with transaction.atomic():
Choice.objects.bulk_create(new_choice_list)
except IntegrityError as e:
raise IntegrityError
return redirect(reverse('question-detail-view', kwargs={'id': question.id}))
def question_detail(request, id):
question_list = Question.objects.get(id=id)
return render(request, 'question_detail.html', {'question_list': question_list})
urls.py
url(r'^question/$', new_question, name='new-question-view'),
url(r'^question/(?P<id>\d+)/$', question_detail, name='question-detail-view'),
If you want to use rather Ajax submission rather than django form sumbission check this tutoriel.

what is Django formsets used for?

I found something strange in Django's documentation which is called formset, see it here.
I am wondering What is formset used for and How to use it?
Formset is an example of datagrid .
If you want to use multiple form of same type at one page.you can use Formset.
Example :
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ["username", "email"]
Now if you want to use UserForm multiple times at one page you need to use Formset.
from django.forms.formsets import formset_factory
Uforms = formset_factory(UserForm, extra = 4) #extra used to define how many empty forms will display
Into Your views.py
def submit(request):
if request.POST:
#code to manage post request
# validation to formset you can follow django docs
else:
address_formSet = Uforms(instance=UserForm())
# render response
Template code
{{ address_formset.as_table }}
You can follow step by step django formset to learn.
Example Code
class Address(models.Model):
city = models.CharField(max_length=48)
zipcode = models.IntegerField(max_length=5)
class Friend(models.Model):
name = models.CharField(max_length=30)
address = models.ForeignKey(Address)
forms.py
from django import forms
from .models import Address, Friend
from django.forms.models import inlineformset_factory
MAX_ADDRESS = 2 #updated
AddressFormSet = inlineformset_factory(Address, Friend, extra=MAX_ADDRESS) #updated
class UserAddressForm(forms.ModelForm):
class Meta:
model = Address
view.py
from django.shortcuts import render_to_response
from .models import *
from .forms import UserSubmittedAddressForm, AddressFormSet
def submit(request):
if request.POST:
#Logic
else:
form = UserAddressForm()
address_formSet = AddressFormSet(instance=Address())
# render response
template code
{{ form.as_table }}
{{ address_formset.as_table }}
It's used to work with e.g. a table of records. It's a way to create data grid functionality in such a way that Django does all the heavy lifting (all data for all the records are sent back in the same POST).

Categories

Resources