I have a ModelForm for a parent model and and an InlineForm for a foreign key related model. The Inline form data is read only and can only be updated via a file upload. I have this working on the intial creation and save, but if you replace the students by uploading a new file, while the data updates properly after the upload and save, the inline form doesn't reload properly right after this save and displays an error. However, if you navigate away and return everything looks good.
Models.py
class Room(models.Model):
room_id = models.AutoField(primary_key=True)
name = models.TextField(blank=False, verbose_name = "Room Name ")
code = models.TextField(blank=False)
file = models.TextField(blank=False)
class Meta(object):
managed = False
db_table = 'public\".\"room'
class Student(models.Model):
room = models.ForeignKey(Room, related_name="students, to_field="room_id", db_column="room_id")
student_id = models.TextField(blank=False, primary_key=False)
#property
def grade(self):
return util.get_grade(self.student_id)
class Meta(object):
managed = False
db_table = 'public\".\"student’
admin.py
class StudentsInline(admin.TabularInline):
model = Student
form = StudentInlineForm
formset = StudentFormSet
readonly_fields = ('student_id', 'grade')
extra = 0
def get_actions(self, request):
'''
Removes the delete action
'''
actions = super(StudentsInline, self).get_actions(request)
del actions['delete_selected']
return actions
def has_delete_permission(self, request, obj=None):
return False
def has_add_permission(self, request, obj=None):
return False
class RoomAdmin(admin.ModelAdmin):
def student_count(self, obj):
return obj.students.count()
student_count.short_description = " Total Enrolled "
form = RoomForm
list_display = [name', ‘code']
readonly_fields = ['student_count']
list_per_page = 25
inlines = [StudentsInline]
def get_actions(self, request):
actions = super(RoomAdmin, self).get_actions(request)
del actions['delete_selected']
return actions
def has_delete_permission(self, request, obj=None):
return False
def save_formset(self, request, form, formset, change):
form.instance.file = 'processed file'
form.instance.save
formset.save()
forms_admin.py
class RoomsForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(RoomsForm, self).__init__(*args, **kwargs)
self.fields[‘name'].required = True
self.fields[‘code'].required = True
self.fields['file'] = forms.FileField(
label='Upload your enrollees:'
def load_file(self, upload_file):
student_list = []
data = upload_file.read() #simplified for this example
for s in reader:
s_id = s[0]
new_student = Student(student_id=s_id)
student_list.append(new_student)
return student_list
def save(self, *args, **kwargs):
room_form = super(RoomsForm, self).save(commit=False)
enrollee_list = self.load_file(self.instance.file.file)
if instance.room_id is None:
instance.file = 'uploaded'
instance = super(RoomsForm, self).save(commit=True)
Student.objects.filter(room_id=self.instance.room_id).all().delete()
instance.students.add(*enrollee_list)
return instance
class Meta:
model = Room
fields = '__all__'
class StudentForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(StudentForm, self).__init__(*args, **kwargs)
Related
I have below models.py and admin.py files in Django. I wanted to do 2 things
Merge specs field of Environment and ItemObject and store into specs of Environment which I managed to do because I was able to figure out where to place the logic. (class AddEnvironmentDetailsInlineForm(forms.ModelForm))
I need to do the same for Estimate. I need to fetch the Environment.specs and Estimate.specs, merge the two and save in Estimate.specs
The merge is like ItemObject -> Environment -> Estimate
The challenge is that I cannot figure out where to put that logic for Estimate and Environment. Do I need to create a ModelForm for Estimate to achieve a merge? I am not clear on the logic here for the files (I am still learning the concepts in Django). If anyone could make me understand, it would be great.
models.py
class Estimate(CommonModel):
...
class Environment(CommonModel):
estimate = models.ForeignKey(Estimate,related_name='environments')
logic = PythonCodeField(blank=True, null=True)
...
class Item(CommonModel):
...
class ItemTemplate(Item):
pass
class ItemObject(Item):
pass
class EnvironmentDetail(models.Model):
environment = models.ForeignKey(Environment)
item_name = models.CharField(max_length=200,blank=True)
qty = models.FloatField('Qty')
...
admin.py
class CommonAdmin(admin.ModelAdmin):
...
class EnvironmentInlineAdmin (admin.TabularInline):
model = Environment
...
formfield_overrides = {
JSONField:{ 'widget':JSONEditor },
}
...
def save_model(self, request, obj, *kwargs):
if request.user.is_superuser or request.user==obj.author:
obj.author.id=request.user.id
super(EnvironmentInlineAdmin,self).save_model(request, obj, *kwargs)
else:
raise ValidationError("author must be you")
class EstimateAdmin (CommonAdmin):
inlines = [ EnvironmentInlineAdmin ]
list_display = ('title', 'gp_code', 'otc_price','annual_price')
admin.site.register(Estimate,EstimateAdmin)
class EnvironmentDetailsInlineForm(forms.ModelForm):
class Meta:
model = EnvironmentDetail
fields = ['item_name','qty']
show_change_link = True
class AddEnvironmentDetailsInlineForm(forms.ModelForm):
class Meta:
model = EnvironmentDetail
fields = ['item_name','item', 'qty']
item = forms.ModelChoiceField(queryset=ItemTemplate.objects.all())
def clean(self):
instance = self.cleaned_data['item']
item_specs = self.cleaned_data['item'].specs
environment_specs = self.cleaned_data['environment'].specs
environment_specs.update(item_specs)
fields = [f.name for f in Item._meta.fields]
values = dict( [(x, getattr(instance, x)) for x in fields] )
new_instance = ItemObject(**values)
new_instance.save() #save new one
#instance.delete() # remove the old one
self.cleaned_data['item'] = new_instance
return self.cleaned_data # Return self.cleaned_data at the end of clean()
def save_model(self, request, obj, *kwargs):
if request.user.is_superuser or request.user==obj.author:
super(AddEnvironmentDetailsInlineForm,self).save_model(request, obj, *kwargs)
else:
raise ValidationError("author must be you")
class EnvironmentDetailsInlineAdmin (admin.TabularInline):
model = EnvironmentDetail
...
form = EnvironmentDetailsInlineForm
formfield_overrides = {
JSONField:{ 'widget':JSONEditor },
}
def change_link(self, obj):
return mark_safe('Edit Item' % \
reverse('admin:estimate_itemobject_change',
args=(obj.item.id,)))
def item_type(self, obj):
return obj.item.item_type
...
class AddEnvironmentDetailsInlineAdmin (admin.TabularInline):
model = EnvironmentDetail
fields = ('item_name','item', 'qty', )
form = AddEnvironmentDetailsInlineForm
def has_change_permission(self, request, obj=None):
return False
def item(self,obj):
return ItemTemplate
class EnvironmentAdmin (CommonAdmin):
model=Environment
save_as = True
inlines = [ EnvironmentDetailsInlineAdmin,AddEnvironmentDetailsInlineAdmin]
...
def get_model_perms(self, request):
"""
Return empty perms dict thus hiding the model from admin index.
"""
return {}
admin.site.register(Environment,EnvironmentAdmin)
I tried below but now this(dict_merge) needs to be saved to Estimate
#receiver(post_save, sender=Estimate)
def update_specs_estimate(sender, instance, **kwargs):
print("instance", instance.specs)
return instance.specs
#receiver(post_save, sender=Environment)
def take_specs_environment(sender, instance, **kwargs):
print("instance", instance.specs)
return instance.specs
def dict_merge(update_specs_estimate, take_specs_environment):
return (update_specs_estimate.update(take_specs_environment))
I am using ModelForm to allow multiple rows edit at the same time. It is a very simple form that has series of yes_no columns. The model looks like:
models.py
class Yn(models.Model):
yn_id = models.IntegerField(primary_key=True)
description = models.CharField(max_length=30)
def __str__(self):
return ' '.join([
self.description,
])
class Meta:
managed = False
db_table = 'yn'
class Invoice(models.Model):
description = models.CharField(max_length=50)
invoice_date = models.DateTimeField()
...
invoice_sent_yn = models.ForeignKey('Yn', models.DO_NOTHING, db_column='invoice_sent_yn', related_name="invoice_sent_yn")
confirm_receipt_yn = models.ForeignKey('Yn', models.DO_NOTHING, db_column='confirm_receipt_yn', related_name="confirm_receipt_yn")
paid_yn = models.ForeignKey('Yn', models.DO_NOTHING, db_column='paid_yn', related_name="paid_yn")
forms.py
class InvoiceGridEdit(ModelForm):
model = Invoice
fields = ['description','invoice_date','invoice_sent_yn', 'confirm_receipt_yn', 'paid_yn']
def __init__(self, *args, **kwargs):
super(JurisGridEditForm, self).__init__(*args, **kwargs)
...
...
InvoiceFormSet = modelformset_factory(models.Invoice, form=InvoiceGridEdit)
views.py
class InvoiceUpdateGrid(CreateView):
template_name = "/invoice_update_grid.html"
model = Invoice
form_class = InvoviceViewEditForm
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return redirect("account_login")
def get(self, request, *args, *kwargs):
self.object = None
customer_number = self.request.GET.get('customer_number')
invoice_form = InvoiceFormSet(queryset=Invoice.objects.filter(customer_number = customer_number)
return self.render_to_response(self.get_context_data(invoice_form=invoice_form))
def post(self, request, *args, **kwargs):
self.object = None
invoice_form = InvoiceFormSet(self.request.POST)
if (invoice_form.is_valid()):
return self.form_valid(invoice_form)
else:
return self.form_invalid(invoice_form)
def form_valid(self, invoice_form):
...
...
invoice_form.save()
return redirect("customer_list")
def form_invalid(self, invoice_form):
return self.render_to_response(self.get_context_data(invoice_form=invoice_form))
The forms works fine, get and post works, except, it takes a while (~30 sec) to retrieve and update. Using django-debug-toolbar, it looks like the yn columns retrieve separately for each column for each row (~2k rows). The Yn table only has 3 rows -1 - Unknown, 0 - No, 1 - Yes.
I tried to search for work around to stop the craziness of Django hitting the DB 900 times per retrieval. I found something about caching but I have no idea how to do it.
Thanks in advance.
I am building a custom tags plugin for django-wiki using django-taggit to save tags. I cannot save the form with .save_m2m as it says in the django-taggit docs. The tags can be saved in the admin but not through the fronted form because I cant save with .save_m2m(). I get the following error even after the form has been saved. Any help would be appreciated.
type object 'SidebarForm' has no attribute 'save_m2m'
wiki_plugins.py
class TagPlugin(BasePlugin):
slug = settings.SLUG
sidebar = {
'headline': _('Tags'),
'icon_class': 'fa-picture-o',
'template': 'sidebar.html',
'form_class': forms.SidebarForm,
'get_form_kwargs': (lambda a: {'instance': models.Tag(article=a)})
}
models.py
class TagRevision(RevisionPluginRevision):
def can_write(self, user):
if not settings.ANONYMOUS and (not user or user.is_anonymous()):
return False
return RevisionPlugin.can_write(self, user)
def can_delete(self, user):
return self.can_write(user)
class Meta:
verbose_name = _('tag')
verbose_name_plural = _('tags')
db_table = 'wiki__plugins_tags_tag' # Matches label of upcoming 0.1 release
if settings.APP_LABEL:
app_label = settings.APP_LABEL
def __str__(self):
if self.current_revision:
return ugettext('Tag: %s') % self.current_revision.tagrevision
else:
return ugettext('Current revision not set!!')
#python_2_unicode_compatible
class TagRevision(RevisionPluginRevision):
tags = TaggableManager()
def inherit_predecessor(self, tag):
"""
Inherit certain properties from predecessor because it's very
convenient. Remember to always call this method before
setting properties :)"""
predecessor = tag.current_revision.tagrevision
self.plugin = predecessor.plugin
self.deleted = predecessor.deleted
self.locked = predecessor.locked
self.tag = predecessor.tag
if not skip_image_file:
try:
self.tags = predecessor.tags
except IOError:
self.tags = None
class Meta:
verbose_name = _('tag revision')
verbose_name_plural = _('tag revisions')
# Matches label of upcoming 0.1 release
db_table = 'wiki_plugins_tags_tagrevision'
if settings.APP_LABEL:
app_label = settings.APP_LABEL
ordering = ('-created',)
def __str__(self):
return ugettext('Tag Revsion: %d') % self.revision_number
forms.py
from wiki_plugins_tags import models
class SidebarForm(PluginSidebarFormMixin):
def __init__(self, article, request, *args, **kwargs):
self.article = article
self.request = request
super(SidebarForm, self).__init__(*args, **kwargs)
self.fields['tags'].required = True
def save(self, *args, **kwargs):
if not self.instance.id:
tagged = models.Tag()
tagged.article = self.article
kwargs['commit'] = False
revision = super(SidebarForm, self).save(*args, **kwargs)
revision.set_from_request(self.request)
tagged.add_revision(self.instance, save=True)
return revision
return super(SidebarForm, self).save(*args, **kwargs)
class Meta:
model = models.TagRevision
fields = ('tags',)
class RevisionForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.tags = kwargs.pop('tags')
self.request = kwargs.pop('request')
super(RevisionForm, self).__init__(*args, **kwargs)
self.fields['tags'].required = True
def save(self, *args, **kwargs):
if not self.instance.id:
kwargs['commit'] = False
revision = super(RevisionForm, self).save(*args, **kwargs)
revision.inherit_predecessor(self.tags, skip_image_file=True)
revision.deleted = False # Restore automatically if deleted
revision.set_from_request(self.request)
self.tags.add_revision(self.instance, save=True)
return revision
return super(RevisionForm, self).save(*args, **kwargs)
class Meta:
model = models.TagRevision
fields = ('tags',)
I have two step of my WizardView process based ModelForm. I don't understand why, when I valide the second step django says me that a previous field are required. I try to send data between step like that:
forms.py
class RequestForm1(forms.ModelForm):
class Meta:
model = Product
fields = ('title', 'product_class', )
class RequestForm2(forms.ModelForm):
class Meta:
model = Product
fields = ( 'dimension_x', )
views.py
class RequestView(SessionWizardView):
instance = None
def get_template_names(self):
return [TEMPLATES[self.steps.current]]
def get_form_initial(self, step):
current_step = self.storage.current_step
if current_step == 'step2':
prev_data = self.storage.get_step_data('step1')
print(prev_data)
title = prev_data.get('step1-title', '')
product_class = prev_data.get('step1-product_class', '')
return self.initial_dict.get(step, {
'title': title,
'product_class': product_class
})
return self.initial_dict.get(step, {})
def done( self, form_list, **kwargs ):
self.instance.save()
return HttpResponseRedirect('/catalogue/request/')
Please find below my solution
forms.py
class RequestForm1(forms.ModelForm):
class Meta:
model = Product
fields = ('title', 'product_class', )
class RequestForm2(forms.ModelForm):
class Meta:
model = Product
fields = ( 'title', 'product_class', 'dimension_x', )
widgets = {
'title': forms.HiddenInput(),
'product_class': forms.HiddenInput()
}
views.py
class RequestView(SessionWizardView):
instance = None
def get_template_names(self):
return [TEMPLATES[self.steps.current]]
def get_form_instance( self, step ):
if self.instance is None:
self.instance = Product()
return self.instance
def get_form(self, step=None, data=None, files=None):
form = super(RequestView, self).get_form(step, data, files)
if step == 'step2':
prev_data = self.storage.get_step_data('step1')
title = prev_data.get('step1-title', '')
product_class = prev_data.get('step1-product_class', '')
form.fields['title'].initial = title
form.fields['product_class'].initial = product_class
return form
def done(self, form_list, **kwargs):
self.instance.save()
return HttpResponseRedirect('/catalogue/request/success')
The admin.py is as follows :-
class SiteDetailInline(admin.TabularInline):
model = SiteDetail
form = SiteDetailForm
fields = ('name', )
can_delete = False
extra = 1
max_num = 1
def get_readonly_fields(self, request, obj=None):
if obj:
return ('clmsid',) + self.readonly_fields
return self.readonly_fields
class SiteAdmin(admin.ModelAdmin):
inlines = [ SiteDetailInline, ]
def queryset(self, queryset):
return Site.objects.filter(~Q(id = settings.SITE_ID))
signals.post_save.connect(create_sites_default_user, sender=Site)
admin.site.unregister(Site)
admin.site.register(Site, SiteAdmin)
The models.py is as follows :-
class SiteDetail(models.Model):
name = models.CharField(max_length=100, unique=True)
client = models.ForeignKey(client)
site = models.ForeignKey(Site)
clmsid = models.CharField(max_length=15, unique=True, verbose_name='clms id', help_text='clms identifier', ) # unique identifier L-XXXXXX-id
def save(self, *args, **kwargs):
if "L-" != self.clmsid[:2]:
self.clmsid = "%s-%s-%s" % ("L", self.accountid, self.id)
super(SiteDetail, self).save(*args, **kwargs)
def __unicode__(self):
return u''
I want to show the extra site details inline in the admin for the site framework. It is not giving any error. However the site details are not displayed inline. Please let me know, what mistake am I doing. Thanks in advance.
Try this
def get_fields(self, request, obj=None):
if obj:
return ('clmsid',) + self.fields
return self.fields