cannot save MultipleChoiceField in django - django

I am having problems saving a MultipleChoiceField on my test server (with Apache). However, it works on my local machine (django server).
In a form, I have checkboxes that I can check and uncheck. When I click on a save button, the data related to the checkboxes are saved in the database and the form is reloaded and the checkboxes updated.
However, this is how it works in local but not on the test server. On the test server, when I click on the save button, it just reloads the form, nothing is saved, nothing is changed.
Here is the code:
class Participant(models.Model):
databases = models.ManyToManyField(Advertiser, null=True, blank=True, through='ShareDataToBrands')
#property
def share_to_brands_list(self):
brands=[]
for brand in ShareDataToBrands.objects.all():
brands.append((brand.advertiser.id, brand.advertiser.brand_name, brand.share_data))
return brands
class ShareDataToBrandsForm(forms.ModelForm):
class Meta:
model = models.Participant
fields = ('databases', )
databases=forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, required=False)
def save(self, *args, **kwargs):
instance=super(ShareDataToBrandsForm, self).save(commit=False)
#list of brands to share data with
share_list=map(int, self.cleaned_data.get("databases"))
participant=self.instance
for share_data_instance in models.ShareDataToBrands.objects.filter(participant=participant):
if share_data_instance.advertiser.id in share_list:
share_data=True
else:
share_data=False
#update record
share_data_instance.share_data=share_data
share_data_instance.save()
return instance
What could possibly be wrong?
EDIT :
When I check the log, I see that the program never enters in the for loop! However, I have records in the database matching the filter.

I have found the solution, looping through theShareDataToBrands instances related to the participant instead of all the ShareDataToBrands instances.
In the share_to_brands_list property, have changed the following line:
for brand in ShareDataToBrands.objects.all():
with
for brand in ShareDataToBrands.objects.filter(participant=self):

Related

Django Manytomany add

I have 2 models in my Project with Many to Many relationship. On saving model Event, I read from the event_attendees file and add it to the attendees field in the Event. No errors/exceptions shown but attendee is not added to the attendees field. Do I need to save the model again after altering with the attendees field? If so, how to do that (calling save method from add_attendees will cause the program into infinite loop)?
class Attendee(models.Model):
name = models.CharField(max_length=100)
class Event(models.Model):
name = models.CharField(max_length=100)
event_attendees = models.FileField(upload_to='documents/', blank=True)
attendees = models.ManyToManyField(Attendee, blank=True)
def save(self, *args, **kwargs):
super().save()
self.add_attendees()
def add_attendees(self):
with open(self.event_attendees.url[1:]) as csv_file:
# Some code here
for row in csv_reader:
# Some code here
attendee = Attendee(name=name)
attendee.save()
self.attendees.add(attendee)
print(self.attendees.all()) # attendee added
print(attendee.event_attended) # event present with attendee
#Refresh template to check changes -> Changes lost
It's the Attendee object that you haven't saved.
You can shortcut it by using the create method on the m2m field:
for row in csv_reader:
self.attendees.create(name=whatever)
(Note, please don't blindly catch exceptions. Django will already do that and report a useful error page. Only catch the exceptions you are actually going to deal with.)
Apparently, the feature worked when I used non-admin web dashboard. While using by-default-created /admin dashboard, this feature was not working. I am assuming from the results that the admin side code calls different methods while saving the model object even though I have overridden the save method (and hence my save method along with other methods should be called). I will update with more info if I find it.

Set current admin user as user to save in db under Django Admin TabularInline

I have a TabularInline under my admin page, and I want to prevent admins from editing submit_user field by making it read-only.
However when creating a new entries in database, I need to be able to pass the current user as submit_user
I've read many posts on stackoverflow but none of them works for me. Many suggested save_formset but it's simply not working for me. The db always complains about not having the submit_user field (if this case, the column has a ForeignKey that maps to another table User, and the complaint is about user_id. Thought have the user object should be fine, but it doesn't seem like it's the case.)
my model looks like
class SomeTable(model.Model):
submit_user = models.ForeignKey(User, db_column='user')
data1 = models.CharField(max_length=200)
create_date = models.DateTimeField(null=False, default=datetime.datetime.now)
So I sorta cheated but solved my issue. Since the table is asking for id instead of user object, I pass in request.user.id instead.
Here's what it looks like
instances = formset.save(commit=False)
for i in instances:
i.submit_user_id = request.user.id
i.save()
formset.save_m2m()
EDIT 1:
This is the final version of my answer. I could successfully add and delete entry with this rewrite of save_formset
def save_formset(self, request, form, formset, change):
instances = formset.save(commit=False)
for i in instances:
if hasattr(i, 'submit_user_id') and not i.submit_user_id:
i.submit_user_id = request.user.id
i.save()
formset.save_m2m()
super(SomeModelAdmin, self).save_formset(request, form, formset, change)

Django AttributeError Modifying Field Without Tracking Change History

I am currently running a production Django webapp that holds historic item information using the SimpleHistory feature.
I have a navigation bar that displays all current revisions of all items to click on and view their separate pages. That being said, I wanted to have the ability to select which items to show/hide in the navigation bar by updating a boolean field on the item admin pages.
So, I modified the item models to have a field to do such:
class Item(models.Model)
field1 = models.CharField()
field2 = models.CharField()
...
hide_item = models.BooleanField('Item hidden:', default=True) #don't want history on this field
reason_for_change = models.CharField()
changed_by = models.ForeignKey(User, null=True)
accepted_by = models.ForeignKey(User, null=True)
accepted_date = models.DateTimeField()
history = HistoricalRecords()
def __unicode__(self):
return self.name
def save(self, *args, **kwargs):
super(Item, self).save(*args, **kwargs)
#property
def _history_user(self):
return self.changed_by
#_history_user.setter
self.changed_by = value
After making the migrations, this field showed up in the admin pages to my delight, but unfortunately I am not able to modify this field without receiving the following error:
AttributeError: can't set attribute
C:\Python27\lib\site-packages\simple_history\admin.py in save_model, line 151
151. obj._history_user = request.user
I think it might have to do with the fact that all changes to item field need to be tracked using the SimpleHistory feature, but for this particular field I don't want to track and store the history of its changes, I just want to be able to enable and disable at will in the admin page.
I also noticed, that if I make a new instance of an Item on the webapp and check the value of hide_item on the admin item page, it is False when it should be True by default. On the contrary, if I attempt to add a new Item instance within the admin page, hide_item is set to True by default as expected...
Right now I think my best solution might be to make another model that holds hide/display information for all items and keep it separate from the item models.
Wondering if anyone might now how to accomplish this.
Thanks
Might not be the most elegant way to do it, but I ended up making a separate model that stores show/hide information and syncs with the item to be displayed.
I did this using a BooleanField for the show/hide and a readonly OneToOne(Item) field to sync with the item I want to display.
Worked well.

Django, auto setting a field during a save, based on other Admin page inputs

I'm looking for the correct way to set full_name in SuperPerson instance.
class Suffix(models.Mode):
suffix = models.CharField(max_length=255)
def __unicode__(self):
return u'%s'%(self.suffix)
class Person(models.Model):
first_name= models.CharField(max_length=255)
last_name= models.CharField(max_length=255)
suffixes= models.ManyToManyField(Suffix, blank=True, null=True)
full_name= models.CharField(max_length=255)
class SuperPerson(Person):
ignore_this_field= model.CharField(max_length=255)
full_name is hidden from the user on the Admin page, and is to be automatically be updated based on the other inputs on Admin page when the Admin page save button is hit.
I have tried overriding save like this and variations:
def save(self, *args, **kwargs):
# Attempt to get data into the database so I can access it
super(SuperPerson,self).save(*args,**kwargs)
self.full_name = self.first_name + self.last_name
for suf in self.suffixes.all():
self.full_name+= suf.__unicode__()
# Now save the copy with full_name set as I wish
super(SuperPerson,self).save(*args,**kwargs)
This method works if I hit the save button in the Admin page twice, which is unacceptable for my use cases, seems like the new self.suffixes I have entered from the Admin page hasn't made it into database with the first super.save when I call self.suffixes.all().
I tried making full_name a property with decorator, but I also need to be able to filter Person and SuperPerson dbs using full_name, so that didn't work, unless someone can tell me how to filter with a property. Though I would rather have the value saved in the db.
I tried pre_save and post_save signals - neither worked.
#receiver(pre_save, sender=SuperPerson)
def set_full_name(sender, instance, **kwargs):
instance.full_name = instance.first_name + instance.last_name
for suf in instance.suffixes.all():
instance.full_name+= ', ' + suf.__unicode__()
Edit: - this has same effect - the instance suffixes do not match what was in the Admin page.
What is the right way to save full_name based on other inputs? Oh and I'm hoping to avoid messing with Admin forms.
ADDITIONAL INFORMATION:
It seems the problem is specifically that the suffixes field is not being updated by the time I'm trying to use it. I can update full_name to something else, like appending a string representing the current date, I just cannot access the suffixes.
Thanks, Dale
SOLUTION:
#receiver(m2m_changed, sender=Person.suffixes.through)
def set_full_name_after_ManyToMany_saved(sender, instance, **kwargs):
instance.full_name = instance.first_name + instance.last_name
for suf in instance.suffixes.all():
instance.full_name+= ', ' + suf.__unicode__()
print 'Saving As', instance.full_name
instance.save()
I'm curious why I had to use Person.suffixes.through instead of SuperPerson, Suffixes or Person.suffixes - is there good documentation on this somewhere, I couldn't find it. And, it runs the code 4 times, but at least ends up with the correct result in the final run.
Many thanks to Danny and burhan
The problem is your m2m relationship with Suffix, or rather the way that django admin saves m2m relationships.
A pretty good explanation is in this answer to Why is adding site to an object doesn't seem to work in a save() override in the Django admin?
When you save a model via admin forms it's not an atomic transaction. The main object gets saved first (to make sure it has a PK), then the M2M is cleared and the new values set to whatever came out of the form.
post_save() is actually still too early. That's where the instance was saved, not its relationships.
You need to connect to the m2m_changed signal: https://docs.djangoproject.com/en/dev/ref/signals/#m2m-changed
or wait for Django 1.4 where ModelAdmin gives you a "when all is done" signal:
https://code.djangoproject.com/ticket/16115

Django Form reload data

I have a form that enters data to db.
I have another form with a drop down field that uses the data entered by the first form.
So when I submit data from the first form,the db is updated properly.
But when I load the second form the drop down is not updated with the latest data.
Steps followed for debugging
The problem is not with transaction/commit etc.
The query to retrieve the data for the drop down in second form is correct.
The problem is not with view cache either(cos we don't have any cache middleware)
I also tried the cache decorators like #never_cahce,#cache_control etc
I tried giving a print statement in second form.
I believe that the problem is with form cache.
Every django form is loaded only once,ie. while loading the first page of the site.
Afterwards the form is loaded from this cache.
First page
form
class AddOrganization(forms.Form):
orgList = getOrgUnitList()
orgUnit = forms.CharField(label=u'Organization Name',
max_length=50,
error_messages={'required':'Organization name is required field.'})
parentOrg= forms.ChoiceField(label=u'Parent Organization',
choices=[(u'Select',u'Select')]+orgList,
error_messages={'required':'Organization name is required field.'})
Second page
form
class AddUser(forms.Form):
orgUnitList = getOrgUnitList()
email = forms.EmailField(label=u'Email',
max_length=50,
error_messages={'required':'Email is required field'})
orgUnit = forms.ChoiceField(label=u'Organizational Unit',
choices=orgUnitList,
error_messages={'required':'Organizational unit is required field'})
Query
def getOrgUnitList():
orgUnitList = list(OrganizationUnit.objects.values_list('OrgUnitID','OrgUnitName').order_by('OrgUnitName'))
return orgUnitList
EDIT
Everything is just fine if I use modelforms.Why So?
The issue is the declaration of orgUnitList as a class property in the form. This means it's called once, when the form is originally defined. So no new elements will be seen, until the server process is restarted.
One way to fix this would be to call the getOrgUnitList function inside the __init__ method of the form:
class AddOrganization(forms.Form):
...
def __init__(self, *args, **kwargs):
super(AddOrganizationForm, self).__init__(*args, **kwargs)
self.fields['orgUnit'].choices = getOrgUnitList()
Alternatively, you should look into using ModelChoiceField for orgUnit, as it deals with this sort of thing automatically.