Django multiple included forms with foreign key - python

i have a problem. Here is 2 related models:
class Auto(models.Model):
...
class Part(models.Model):
...
parent = models.ForeignKey(Auto, blank = False, null = False)
So, i want to create next django form:
Auto1.field1 Auto1.Part1.field1 Auto1.field2
Auto1.Part1.field2
Auto1.Part2.field1
Auto1.Part2.field2
...
Auto2.field1 Auto2.Part1.field1 Auto2.field2
Auto2.Part1.field2
Auto2.Part2.field1
Auto2.Part2.field2
...
...
All fields must be updatable, as for model Auto and for model Part.
How can i do this?

You should try to use Inline formsets. For example, assuming you have an Auto modelform it can be done like this:
autoforms = []
part_formsets = []
autos = Auto.objects.all()
PartFormSet = inlineformset_factory(Auto, Part, fields=('field1', 'field2'))
for auto in autos:
autoform = AutoForm(instance=auto)
autoforms.append(autoform)
part_formset = PartFormSet(instance=auto)
part_formsets.append(part_formset)
c = {'autoforms': autoforms, 'part_formsets': part_formsets}
return render(request, 'some.html', c)
Now you can iterate autoforms and part_formsets in template to get the desired forms. However, I have not checked if it is easy to render them in a template as you want.

Related

displaying one field from InlineModel horizontally in django-admin

i have field('image_tag') from Inlinemodel that i want to display in one row of Orderdetail model.
class SampleImagesInline(admin.StackedInline):
fields = ['image_tag']
readonly_fields = ['image_tag']
model = SampleImages
extra = 0
#admin.register(OrderDetail)
class OrderDetailAdmin(admin.ModelAdmin):
inlines = [SampleImagesInline]
by default these are showing vertically. how to display in one row?.
You can use TabularInline. Try like this:
class SampleImagesInline(admin.TabularInline):
fields = ['image_tag']
readonly_fields = ['image_tag']
model = SampleImages
extra = 0
Update
I think I misunderstood your problem. IMHO, you should not use the InLineAdmin. Instead, try like this:
from django.utils.safestring import mark_safe
...
class OrderDetailAdmin(admin.ModelAdmin):
...
readonly_fields = ['image_tags',]
def image_tags(self, obj):
img_html = ""
for image in obj.image_set.all(): # <-- get related images
img_html += "<img src={}> ".format(image.image.url)
same_line_html = '<div class="tabular inline-related last-related">{}</div>'.format(img_html)
return mark_safe(same_line_html)
image_tags.description = "Images"
Please see here in docs for more information on getting related objects

I want to crate a Django field in my model to display a word after each new entry

So I want to create a field in my Django models.py, so that the user can select a number of years as integer (e.g. 3) and then after each new entry the words 'years' to be automatically displayed. I do not want to do this with CharField.
class StudyProgramme(models.Model):
period = models.IntegerField(2)
You can write a serializer for the same.
from rest_framework import serializers
class StudyProgrammeSerializer(serializers.ModelSerializer):
def get_period(self, obj):
return str(obj.period) + "years"
class Meta:
model = StudyProgramme
fields = ['period']
I don't get it yet but i think you can do it like:
# Check the model StudyProgramme
If StudyProgramme.Objects.all().count() > 0: //Show
Else: //request to put it it
# And for sure, return it to template

django-simple-history, displaying changed fields in admin

When I inherit from admin.ModelAdmin, in history on admin page I can see what fields has been changed. However, now I need to use django-simple-history to track all my model changes. Now, for admin, I inherit for simple_history.SimpleHistoryAdmin. Whilst I can see all of the model changes and revert them, I cannot see, which fields were changed. Is it possible to add that handy functionality to SimpleHistoryAdmin?
I found a way to solve this issue. I added a ModelAdmin method and used History Diffing to add a custom field in the Change history table.
history_list_display = ['changed_fields']
def changed_fields(self, obj):
if obj.prev_record:
delta = obj.diff_against(obj.prev_record)
return delta.changed_fields
return None
What you need is history_list_display field in your Admin. The list of fields included in the history_list_display will be displayed in the history page with their corresponding entries.
Something like this:
class SomeAdmin(admin.ModelAdmin):
def some_user_defined(self, obj):
return "something"
date_hierarchy = 'created_at'
search_fields = ['field1', 'field2']
list_display = ('field1', 'field2',)
list_filter = ('field1',)
history_list_display = ('field1', 'field2', 'some_user_defined',)
This will display field1, field2 along with comment, user and reason
You probably want to do something like that:
# admin.py
from django.contrib import admin
from simple_history.admin import SimpleHistoryAdmin
from .models import Website
from django.utils.html import format_html
class WebsiteHistoryAdmin(SimpleHistoryAdmin):
history_list_display = ["changed_fields","list_changes"]
def changed_fields(self, obj):
if obj.prev_record:
delta = obj.diff_against(obj.prev_record)
return delta.changed_fields
return None
def list_changes(self, obj):
fields = ""
if obj.prev_record:
delta = obj.diff_against(obj.prev_record)
for change in delta.changes:
fields += str("<strong>{}</strong> changed from <span style='background-color:#ffb5ad'>{}</span> to <span style='background-color:#b3f7ab'>{}</span> . <br/>".format(change.field, change.old, change.new))
return format_html(fields)
return None
admin.site.register(Website, WebsiteHistoryAdmin)
And you get this as a result:
And if you want to view not only names of changed fields as per Rafi comment and also changed values, next code will do it:
def changed_fields_with_values(self, obj):
fields = ""
if obj.prev_record:
delta = obj.diff_against(obj.prev_record)
for change in delta.changes:
fields += str("{} changed from {} to {}".format(change.field, change.old, change.new))
return fields
return None
Similar to the previous solution from Rafi but using array to list more elegantly the record changes:
def list_changes(self, obj):
diff = []
if obj.prev_record:
delta = obj.diff_against(obj.prev_record)
for change in delta.changes:
diff.append("<b>* {}:</b> changed from `{}` to `{}`".format(change.field, change.old, change.new))
return mark_safe("\n<br>".join(diff))

iterate over django form results (not in a template)

I am trying to iterate over form results and I can't help but think that I am re-inventing the wheel here.
filterlist = []
if request.POST:
form = FilterForm(request.POST)
if form.is_valid():
for key, value in form.cleaned_data.iteritems():
filterlist.append(key)
filterlist.append(value)
This works, but seems very awkward and creates lots of other problems. For example the values come back with u' so I have to use value.encode("utf8") but then if a value is None it throws in error. So now I have to check if it is None, if not then encode. There has to be a better way.
EDIT: What I am trying to do.
I am trying to filter what is shown on a page. The problem I am running into is that if a value is empty (the user don't fill the box because they only want to filter against one object) then I get no results. For example a user wants to search for all books by the author name "Smith" but doesn't want to search against a genre.
results = Books.objects.filter(author=author, genre=genre)
The user would get no results because this is an AND search. But, if a user put in "Smith" for the author and "mystery" for the genre then it works exactly like I want it to, only giving results where both are true.
So, I am trying to eliminate the empty stuff by iterating over the form results. Like I said I am probably re-inventing the wheel here.
In Python 3 use:
for key, value in form.cleaned_data.items():
If the field names are the same in the model and the form, try this:
filter = {}
if request.method == 'POST':
form = FilterForm(request.POST)
if form.is_valid():
for key, value in form.cleaned_data.iteritems():
if value:
filter[key] = value
results = Books.objects.filter(**filter)
Python is one of the few languages having named parameters. You can assemble a dict with the non-empty form fields and pass it to the filter method using the kwargs unpacking operator **.
For example:
kwargs = {"author": "Freud"}
results = Books.objects.filter(**kwargs)
Is the same as:
results = Books.objects.filter(author="Freud")
I think the problem is that by default the Model form is not valid if a form field does not have a value entered by the user, if you don`t require the field every time from the user you need to set the required field to false in the ModelForm class in forms.py as shown in the code below. Remember that the field is set false only in the model form not in the model itself
class myForm(forms.ModelForm):
myfield_id = forms.CharField(required=False)
myfield_foo = forms.CharField(required=False)
myfield_bar = forms.CharField(required=False)
myfield_name = forms.CharField(required=False)
class Meta:
model = myModel
exclude = ('myfield_ex','myfield_file')
fields = ['myfield_id','myfield_foo','myfield_bar','myfield_name',]
After you have the form entered by the user what you need is use the Q object which can be used to create complex queries as described in the manula page here
https://docs.djangoproject.com/en/1.7/topics/db/queries/#complex-lookups-with-q
A simple example code would look like
if form.is_valid():
qgroup = []
for key,value in form.cleaned_data.iteritems():
if value:
q_name = Q(**{"%s"%format(filterKey[key]) : value})
qgroup.append(q_name)
q = None
# can use the reduce as shown here qgroup = reduce(operator.or_, (Q(**{"{0}".format(filterKey[key]): value}) for (key,value) in form.cleaned_data.iteritems()))
for key,value in form.cleaned_data.iteritems():
if value:
q_name = Q(**{"%s"%format(filterKey[key]) : value})
qgroup.append(q_name)
for x in qgroup:
q &= x ### Or use the OR operator or
if q:
resultL = myModel.objects.filter(q).select_related()
The filterKey can look something on the lines of
filterKey = {'myfield_id' : "myfield_id",
'myfield_foo' : "myfield_foo__icontains",
'myfield_bar' : "myfield_bar__relative_field__icontains",
}

How do I generate dynamic fields in WTForms

I am trying to generate a form in WTForms that has dynamic fields according to this documentation http://wtforms.simplecodes.com/docs/1.0.2/specific_problems.html#dynamic-form-composition
I have this subform class which allows users to pick items to purchase from a list:
class Item(Form):
itmid = SelectField('Item ID')
qty = IntegerField('Quantity')
class F(Form):
pass
There will be more than one category of shopping items, so I would like to generate a dynamic select field based on what categories the user will choose:
fld = FieldList(FormField(Item))
fld.append_entry()
but I get the following error:
AttributeError: 'UnboundField' object has no attribute 'append_entry'
Am I doing something wrong, or is there no way to accomplish this in WTForms?
I ran into this issue tonight and ended up with this. I hope this helps future people.
RecipeForm.py
class RecipeForm(Form):
category = SelectField('Category', choices=[], coerce=int)
...
views.py
#mod.route('/recipes/create', methods=['POST'])
def validateRecipe():
categories = [(c.id, c.name) for c in g.user.categories.order_by(Category.name).all()]
form = RecipeForm(request.form)
form.category.choices = categories
...
#mod.route('/recipes/create', methods=['GET'])
def createRecipe():
categories = [(c.id, c.name) for c in g.user.categories.order_by(Category.name).all()]
form = RecipeForm(request.form)
form.category.choices = categories
return render_template('recipes/createRecipe.html', form=form)
I found this post helpful as well
class BaseForm(Form):
#classmethod
def append_field(cls, name, field):
setattr(cls, name, field)
return cls
from forms import TestForm
form = TestForm.append_field("do_you_want_fries_with_that",BooleanField('fries'))(obj=db_populate_object)
I use the extended class BaseForm for all my forms and have a convenient append_field function on class.
Returns the class with the field appended, since instances (of Form fields) can't append fields.
Posting without writing full code or testing the code, but maybe it will give you some ideas. Also this could maybe only help with the filling the needed data.
You need to fill choices for SelectField to be able to see the data and be able to select it. Where you fill that? Initial fill should be in the form definition, but if you like dynamic one, I would suggest to modify it in the place where you creating this form for showing to the user. Like the view where you do some form = YourForm() and then passing it to the template.
How to fill form's select field with choices? You must have list of tuples and then something like this:
form.category_select.choices = [(key, categories[key]) for key in categories]
form.category_select.choices.insert(0, ("", "Some default value..."))
categories here must be dictionary containing your categories in format like {1:'One', 2:'Two',...}
So if you will assign something to choices when defining the form it will have that data from the beginning, and where you need to have user's categories, just overwrite it in the view.
Hope that will give you some ideas and you can move forward :)
have you tried calling append_entry() on the form instance instead of the FieldList definition?
class F(Form)
fld = FieldList(SelectField(Item))
form = F()
form.fld.append_entry()
This is how i got it to work.
class MyForm(FlaskForm):
mylist = SelectField('Select Field', choices=[])
#app.route("/test", methods=['GET', 'POST']
def testview():
form = MyForm()
form.mylist.choices = [(str(i), i) for i in range(9)]
Strangely this whole thing stops working for me if i use coerce=int. I am myself a flask beginner, so i am not really sure why coerce=int causes issue.
WTForms Documentation : class wtforms.fields.SelectField
Select fields with dynamic choice values:
class UserDetails(Form):
group_id = SelectField(u'Group', coerce=int)
def edit_user(request, id):
user = User.query.get(id)
form = UserDetails(request.POST, obj=user)
form.group_id.choices = [(g.id, g.name) for g in Group.query.order_by('name')]

Categories

Resources