I want to get a queryset of all books that are currently in Library (the dateReturn of a currently rent is set to null).
I'm new to python and i don't know how to do subqueries in django.
In other words in want to filter every related object field on a condition, if only one related-object doesn't match to this condition the object must not be returned
models.py
class Book(models.Model):
cod = models.CharField(max_length=255, unique=True)
title = models.CharField(max_length=255)
.....
class Rent(models.Model):
dateRent = models.DateField(default=timezone.now)
dateReturn = models.DateField(null=True, blank=True)
book = models.ForeignKey(modelsBook.Book, on_delete=models.DO_NOTHING, related_name="rent")
.....
P.S:
I need this subquery for display book currently not render in a choiceField
forms.py
class RentForm(forms.ModelForm):
__pk=None
def __init__(self, *args, **kwargs):
self.__pk = kwargs.pop('pk', None)
super(RentForm, self).__init__(*args, **kwargs)
class Meta():
model = models.Rent
fields = ('book', 'student')
labels = {
'book' : _('Libro'),
'student' : _('Studente'),
}
widgets = {
'book': queryset,
.....
You can filter objects through the related_name.
class RentForm(forms.ModelForm):
__pk=None
def __init__(self, *args, **kwargs):
self.__pk = kwargs.pop('pk', None)
super(RentForm, self).__init__(*args, **kwargs)
self.fields['book'].queryset = Book.objects.exclude(rent__dateReturn__isnull=True)
...
Related
I need to generate Django forms.Form object with fields not from Model.fields (Database Table Columns names), but by records in Model.Table.
I have table Model in models.py:
class MntClasses(models.Model):
type = models.CharField(max_length=2, blank=True, null=True)
class_subtype = models.CharField(max_length=45, blank=True, null=True)
text = models.CharField(max_length=45, blank=True, null=True)
explanation = models.CharField(max_length=45, blank=True, null=True)
name = models.CharField(max_length=45, blank=True, null=True)
views.py
# Form generate
class Form_classes(forms.Form):
def __int__(self, *args, **kwargs,):
super(Form_classes, self).__init__(*args, **kwargs)
print("some")
for fld_ in args:
self.fields[fld_.name] = forms.BooleanField(label=fld_.text)
#Main
def page_Category_Main(request, post):
db_table = MntClasses
form_fld = db_table.objects.all()
'''
This QuerySet 20 records returned of <MntClasses: MntClasses object (0-19)> type.
QuerySet Filds Names: 'name','type','expalnation', 'text'
''':
form_ = Form_classes(*form_fld)
exit_ = {
'form': form_,
}
return render(request, template_name="category.html", context=exit_)
It raise TypeError
init() takes from 1 to 12 positional arguments but 20 were given
So, i have no idea what does it mean this code taken from were: Auto-generate form fields for a Form in django:
def __int__(self, *args, **kwargs,):
super(Form_classes, self).__init__(*args, **kwargs)
What is this "*args", how to use it?
How can I generate Form.fields by QuerySet form_fld.name in that case?
About args
To understand what args is you can take a look at this post which will eventually direct you to https://docs.python.org/3/tutorial/controlflow.html#arbitrary-argument-lists.
Basically it's a special python syntax which allows a function to retrieve multiple arguments as a tuple in a single variable.
About Django's Models
Do you really want to generate multiple forms?
If this is the case you would need to loop over your table:
forms = []
db_table = MntClasses
for item in db_table.objects.all():
forms.append(Form_classes(item))
exit_ = {
'form': forms,
}
the trick is then how you deal with the multiple forms on the front end.
Also you probably want to switch to a ModelForm which would look something like:
from django import forms
from myapp.models import MntClasses
class FormMnt(forms.ModelForm):
class Meta:
model = MntClasses
...
In case you want to handle multiple instances of MntClasses in a single form, you should look at Django's formsets.
I am still relatively new to Django and still struggle somewhat with ForeignKey filtering and I'd appreciate any help with my problem. I have 2 models below and in my PositionUpdateForm I need the 'candidate' field choices to be only the applicants to that position.
class Position(models.Model):
title = models.CharField(max_length=128)
candidate = models.ForeignKey('careers.Applicant',
on_delete=models.SET_NULL,
related_name='candidates',
blank=True,
null=True
)
class Applicant(models.Model):
first_name = models.CharField(max_length=128)
blank=False,
)
position = models.ManyToManyField(Position,
related_name='applicants',
blank=True
)
In my form I was trying each of the following:
class PositionUpdateForm(forms.ModelForm):
candidate = forms.ModelChoiceField(queryset=Applicant.objects.filter(???))
def __init__(self, *args, **kwargs):
super(PositionUpdateForm, self).__init__(*args, **kwargs)
self.fields['candidate'].queryset = Applicant.objects.filter(???)
Thank you for any assistance.
If you want to have the Applicants that have a position to that Position, you can obtain that with:
class PositionUpdateForm(forms.ModelForm):
candidate = forms.ModelChoiceField(queryset=Applicant.objects.empty())
def __init__(self, *args, **kwargs):
super(PositionUpdateForm, self).__init__(*args, **kwargs)
self.fields['candidate'].queryset = Applicant.objects.filter(position=self.instance)
or we can use the relation in reverse:
class PositionUpdateForm(forms.ModelForm):
candidate = forms.ModelChoiceField(queryset=Applicant.objects.empty())
def __init__(self, *args, **kwargs):
super(PositionUpdateForm, self).__init__(*args, **kwargs)
self.fields['candidate'].queryset = self.instance.applicants.all()
Note that you can only use this when you update a Position model, since otherwise there are no related Applicant records of course.
I'm creating a web application with Django.
In my models.py I have a class BaseProduct and a class DetailProduct, which extends BaseProduct.
In my admin.py I have BaseProductAdmin class and DetailProductAdmin class, which extends BaseProductAdmin.
I have another class called System, with a many to many relation with BaseProduct.
In the System admin page, I can visualize a list of the BaseProduct objects related to that system.
When I click on a product, the application redirect me to the BaseProduct admin page.
When a product of the list is a DetailProduct object, I would like to be redirected on the DetailProduct admin page instead.
Any idea on how to do this?
In models.py :
class BaseProduct(models.Model):
id = models.AutoField(primary_key=True, db_column='ID')
_prod_type_id = models.ForeignKey(
ProductTypes, verbose_name="product type", db_column='_prod_type_ID')
systems = models.ManyToManyField(
'database.System', through='database.SystemProduct')
def connected_to_system(self):
return self.systems.exists()
class Meta:
db_table = u'products'
verbose_name = "Product"
ordering = ['id', ]
class System(models.Model):
id = models.AutoField(primary_key=True, db_column='ID')
name = models.CharField(max_length=300)
def has_related_products(self):
""" Returns True if the system is connected with products. """
return self.products_set.exists()
class Meta:
managed = False
db_table = u'systems'
verbose_name = "System"
ordering = ['id', ]
class DetailProduct(BaseProduct):
options_id = models.AutoField(db_column='ID', primary_key=True)
product = models.OneToOneField(BaseProduct, db_column='_product_ID', parent_link=True)
min_height = models.FloatField(help_text="Minimum height in meters.")
max_height = models.FloatField(help_text="Maximum height in meters.")
def __init__(self, *args, **kwargs):
super(DetailProduct, self).__init__(*args, **kwargs)
if not self.pk:
self._prod_type_id = ProductTypes.objects.get(pk=9)
class Meta:
managed = False
db_table = 'detail_product'
verbose_name = "Detail product"
verbose_name_plural = "Detail products"
class SystemProduct(models.Model):
id = models.AutoField(primary_key=True, db_column='ID')
_system_id = models.ForeignKey(System, db_column='_system_ID')
_product_id = models.ForeignKey(BaseProduct, db_column='_Product_ID')
class Meta:
db_table = u'system_product'
unique_together = ('_system_id', '_product_id')
verbose_name = "system/product connection"
In my admin.py page:
class SystemProductInlineGeneric(admin.TabularInline):
model = SystemProduct
extra = 0
show_edit_link = True
show_url = True
class SystemProductForm(forms.ModelForm):
class Meta:
model = SystemProduct
fields = '__all__'
def __init__(self, *args, **kwargs):
""" Remove the blank option for the inlines. If the user wants to remove
the inline should use the proper delete button. In this way we can
safely check for orphan entries. """
super(SystemProductForm, self).__init__(*args, **kwargs)
modelchoicefields = [field for field_name, field in self.fields.iteritems() if
isinstance(field, forms.ModelChoiceField)]
for field in modelchoicefields:
field.empty_label = None
class SystemProductInlineForSystem(SystemProductInlineGeneric):
""" Custom inline, used under the System change page. Prevents all product-system
connections to be deleted from a product. """
form = SystemProductForm
raw_id_fields = ("_product_id",)
class SystemAdmin(admin.ModelAdmin):
inlines = [SystemProductInlineForSystem]
actions = None
list_display = ('id', 'name')
fieldsets = [('System information',
{'fields': (('id', 'name',), ),}),
]
list_display_links = ('id', 'configuration',)
readonly_fields = ('id',)
save_as = True
If I understand correctly, your question is how to change the InlineAdmin (SystemProductInlineForSystem) template so the "change link" redirects to the DetailProduct admin change form (instead of the BaseProduct admin change form) when the product is actually a DetailProduct.
I never had to deal with this use case so I can't provide a full-blown definitive answer, but basically you will have to override the inlineadmin template for SystemProductInlineForSystem and change the part of the code that generates this url.
I can't tell you exactly which change you will have to make (well, I probably could if I had a couple hours to spend on this but that's not the case so...), so you will have to analyze this part of the code and find out by yourself - unless of course someone more knowledgeable chimes in...
Models.py
class Book(models.Model):
created_at = models.DateTimeField(auto_now_add=True, editable=False)
name = models.CharField(max_length=255, unique=True)
slug = models.SlugField(max_length=255, blank=True, default='', unique=True)
class BookTranslation(models.Model):
book = models.ForeignKey(Book, related_name='book_translations')
language = models.CharField(max_length=2, choices=LANGUAGE_CHOICES)
class Chapter(models.Model):
chapter = models.IntegerField()
book_translation = models.ForeignKey(BookTranslation, related_name='related_chapters')
class Page(models.Model):
chapter = models.ForeignKey(Chapter, related_name='related_pages')
page = models.IntegerField()
url = models.CharField(max_length=255)
urls.py
url(r'^(?P<language>[\w-]{2})-book/(?P<slug>[\w-]+)/(?P<chapter>\d+)/(?P<page>\d+)/$",
PageView(),
name="page")
I tried different approaches to make PageView() and ended up with this:
class PageView(DetailView):
model = Book
template_name = 'bookinfo/book_page.html'
context_object_name = 'book'
def get_context_data(self, **kwargs):
context = super(PageView, self).get_context_data(**kwargs)
context['book_translation'] = BookTranslation.objects.get(
book=context['book'], language=self.kwargs['language'])
context['chapter'] = Chapter.objects.get(
book_translation=context['book_translation'].id, chapter=self.kwargs['chapter'])
context['page'] = Page.objects.get(
chapter=context['chapter'].id, page=self.kwargs['page'])
context['chapter_list'] = Chapter.objects.filter(
book_translation=context['book_translation'].id)
context['page_list'] = Page.objects.filter(
chapter=context['chapter'].id)
return context
But this way, every time i open this page, i make 6 requests to the DB, when i could easily get the arguments in context['book'], context['book_translation'] context['chapter'] and context['page'] with a singole SQL query like this:
SELECT *
FROM Book b, BookTranslation bt, Chapter c, Page p
WHERE b.slug=self.kwargs['slug'] and
b.name=bt.book_id and
bt.language=self.kwargs['language'] and
c.book_translation_id=bt.id and
c.chapter=self.kwargs['chapter'] and
c.id=p.chapter_id and
p.page=self.kwargs['page']
Can someone explain me how can i make this View(preferably class based) more performance? I probably didn't understand how they work...
If you want to retrieve related foreign keys use select_related by overriding get_queryset of SingleObjectMixin as follows:
class PageView(DetailView):
...
def get_queryset(self, *args, **kwargs):
qs = super(PageView, self).get_queryset(*args, **kwargs)
return qs.select_related()
This will fetch all related objects in a single query, so as long as the queryset is not evaluated, all the related objects in your view's get_context_data should not be queried again.
Refer: https://docs.djangoproject.com/en/1.6/ref/models/querysets/#select-related
taking my initial lessons with django ModelForm ,I wanted to give the user ,ability to edit an entry in a blog.The BlogEntry has a date,postedTime, title and content.I want to show the user an editform which shows all these fields,but with only title and content as editable. The date and postedTime should be shown as uneditable.
class BlogEntry(models.Model):
title = models.CharField(unique=True,max_length=50)
description = models.TextField(blank=True)
date = models.DateField(default=datetime.date.today)
postedTime = models.TimeField(null=True)
...
For adding an entry ,I use a ModelForm in the normal way..
class BlogEntryAddForm(ModelForm):
class Meta:
model = BlogEntry
...
But how do I create the edit form?I want it to show the date,postedTime as uneditable (but still show them on the form) and let the user edit the title and description.
if I use,exclude in class Meta for date and postedTime,that will cause them not to appear on the form.So,how can I show them as uneditable?
class BlogEntryEditForm(ModelForm):
class Meta:
model = BlogEntry
...?...
In the form object, declare the attribute of the field as readonly:
form.fields['field'].widget.attrs['readonly'] = True
Is date field represent a date when the entry first created or when it was modified last time? If first then use auto_now_add option else use auto_now. That is:
date = models.DateField(auto_now_add=True)
will set date to now when entry will be created.
auto_now_add makes field uneditable. For other cases use editable option to make any field uneditable. For example
postedDate = models.TimeField(null=True, editable=False)
Also, likely you will add posted boolean field to Entry model, so it is convinient to set auto_now on postedDate. It will set postedDate to now every time you modify a Entry including one when you set posted to True.
I implemented it this way: https://djangosnippets.org/snippets/10514/
this implementation uses the data of model instance for all read-only fields and not the data obtained while processing the form
below the same code but using his example
from __future__ import unicode_literals
from django.utils import six
from django.utils.encoding import force_str
__all__ = (
'ReadOnlyFieldsMixin',
'new_readonly_form_class'
)
class ReadOnlyFieldsMixin(object):
"""Usage:
class MyFormAllFieldsReadOnly(ReadOnlyFieldsMixin, forms.Form):
...
class MyFormSelectedFieldsReadOnly(ReadOnlyFieldsMixin, forms.Form):
readonly_fields = ('field1', 'field2')
...
"""
readonly_fields = ()
def __init__(self, *args, **kwargs):
super(ReadOnlyFieldsMixin, self).__init__(*args, **kwargs)
self.define_readonly_fields(self.fields)
def clean(self):
cleaned_data = super(ReadOnlyFieldsMixin, self).clean()
for field_name, field in six.iteritems(self.fields):
if self._must_be_readonly(field_name):
cleaned_data[field_name] = getattr(self.instance, field_name)
return cleaned_data
def define_readonly_fields(self, field_list):
fields = [field for field_name, field in six.iteritems(field_list)
if self._must_be_readonly(field_name)]
map(lambda field: self._set_readonly(field), fields)
def _all_fields(self):
return not bool(self.readonly_fields)
def _set_readonly(self, field):
field.widget.attrs['disabled'] = 'true'
field.required = False
def _must_be_readonly(self, field_name):
return field_name in self.readonly_fields or self._all_fields()
def new_readonly_form_class(form_class, readonly_fields=()):
name = force_str("ReadOnly{}".format(form_class.__name__))
class_fields = {'readonly_fields': readonly_fields}
return type(name, (ReadOnlyFieldsMixin, form_class), class_fields)
Usage:
class BlogEntry(models.Model):
title = models.CharField(unique=True,max_length=50)
description = models.TextField(blank=True)
date = models.DateField(default=datetime.date.today)
postedTime = models.TimeField(null=True)
# all fields are readonly
class BlogEntryReadOnlyForm(ReadOnlyFieldsMixin, forms.ModelForm):
class Meta:
model = BlogEntry
# selected fields are readonly
class BlogEntryReadOnlyForm2(ReadOnlyFieldsMixin, forms.ModelForm):
readonly_fields = ('date', 'postedTime')
class Meta:
model = BlogEntry
or use the function
class BlogEntryForm(forms.ModelForm):
class Meta:
model = BlogEntry
BlogEntryFormReadOnlyForm = new_readonly_form_class(BlogEntryForm, readonly_fields=('description', ))
This will prevent any user from hacking the request:
self.fields['is_admin'].disabled = True
Custom form example:
class MemberShipInlineForm(forms.ModelForm):
is_admin = forms.BooleanField(required=False)
def __init__(self, *args, **kwargs):
super(MemberShipInlineForm, self).__init__(*args, **kwargs)
if 'instance' in kwargs and kwargs['instance'].is_group_creator:
self.fields['is_admin'].disabled = True
class Meta:
model = MemberShip
fields = '__all__'
From the documentation,
class BlogEntryEditForm(ModelForm):
class Meta:
model = BlogEntry
readonly_fields = ['date','postedTime']