For my first Django project I'm trying to make an app that lets users create lists of media (books, movies, etc.) with various fields describing each object (title, author, etc.), and I'm having trouble figuring out how to get it to save. That is to say that nothing happens when the form is submitted. Can someone tell me what I'm doing wrong? Sorry if this is a bit of a noob question; it seems like I'm missing something really basic here. (I'm using basic HTML forms instead of ModelForms because for some media types I want to ignore certain fields - e.g. "author" for movies - but if there is an easy way to do that using ModelForms, I'm all ears.)
from views.py:
def editbook(request,list_owner,pk):
book_list = Item.objects.all().filter(item_creator=list_owner).filter(category='book').order_by('type','name')
item_to_edit = Item.objects.get(pk=pk)
if request.method == 'POST':
item_to_edit.save()
return render_to_response('books.html', {'booklist': book_list, 'listowner': list_owner}, RequestContext(request))
else:
form=EditItem()
return render_to_response('editbook.html', {'listowner': list_owner, 'item_to_edit': item_to_edit}, RequestContext(request))
from models.py:
CATEGORY_CHOICES = (
('book','book'),
('tv','tv'),
('movie','movie'),
('game','game'),
('music','music'),
)
class Item(models.Model):
item_creator = models.CharField(max_length=30) # user name goes here
category = models.CharField(max_length=5, choices=CATEGORY_CHOICES)
name = models.CharField(max_length=70)
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
artist = models.CharField(max_length=70, blank=True)
type = models.CharField(max_length=50, blank=True)
progress = models.CharField(max_length=10, blank=True)
finished = models.BooleanField(default=False)
rating = models.IntegerField(default=0, blank=True, null=True)
comment = models.CharField(max_length=140, blank=True)
def __unicode__(self):
return self.name
There is, of course, a way to only use some fields in a modelform: as fully documented in Using a subset of fields on the form, you can use the fields or exclude attributes in the form's Meta class.
However you'll still need, as szaman points out, to pass the POST data to the form and check for validity, and in addition you'll need to pass in the instance paramater as you're updating an existing instance.
What I see is that you get object from database and when form is submitted than just saving the object, but you don't update any field so you cannot see changes in db. Try to do:
if request.method == "POST":
form = MyForm(request.POST)
logging.info("form.is_valid() %s" % form.is_valid())
if form.is_valid():
item_to_edit.name = form.cleaned_data['name']
item_to_edit.save()
...
Related
I've got the following Situation, I have a rather large legacy model (which works nonetheless well) and need one of its fields as a distinct dropdown for one of my forms:
Legacy Table:
class SummaryView(models.Model):
...
Period = models.CharField(db_column='Period', max_length=10, blank=True, null=True)
...
def __str__(self):
return self.Period
class Meta:
managed = False # Created from a view. Don't remove.
db_table = 'MC_AUT_SummaryView'
Internal Model:
class BillCycle(models.Model):
...
Name = models.CharField(max_length=100, verbose_name='Name')
Period = models.CharField(max_length=10, null=True, blank=True)
Version = models.FloatField(verbose_name='Version', default=1.0)
Type = models.CharField(max_length=100, verbose_name='Type', choices=billcycle_type_choices)
Association = models.ForeignKey(BillCycleAssociation, on_delete=models.DO_NOTHING)
...
def __str__(self):
return self.Name
Since I don't want to connect them via a Foreign Key (as the SummaryView is not managed by Django) I tried a solution which I already used quite a few times. In my forms I create a ModelChoiceField which points to my Legacy Model:
class BillcycleModelForm(forms.ModelForm):
period_tmp = forms.ModelChoiceField(queryset=SummaryView.objects.values_list('Period', flat=True).distinct(),
required=False, label='Period')
....
class Meta:
model = BillCycle
fields = ['Name', 'Type', 'Association', 'period_tmp']
And in my view I try to over-write the Period Field from my internal Model with users form input:
def billcycle_create(request, template_name='XXX'):
form = BillcycleModelForm(request.POST or None)
data = request.POST.copy()
username = request.user
print("Data:")
print(data)
if form.is_valid():
initial_obj = form.save(commit=False)
initial_obj.ModifiedBy = username
initial_obj.Period = form.cleaned_data['period_tmp']
initial_obj.Status = 'Creating...'
print("initial object:")
print(initial_obj)
form.save()
....
So far so good:
Drop Down is rendered correctly
In my print Statement in the View ("data") I see that the desired infos are there:
'Type': ['Create/Delta'], 'Association': ['CP'], 'period_tmp': ['2019-12']
Still I get a Select a valid choice. That choice is not one of the available choices. Error in the forms. Any ideas??
Disclaimer: I am new at Django so I'm sure my code is ugly.
Problem:
My current Model is built as follows:
class person(models.Model):
email = models.EmailField()
date = models.DateField()
phone_number = models.IntegerField()
name = models.CharField(max_length = 50)
belongs_to_group = models.ForeignKey(Group, related_name='group', on_delete=models.SET_NULL, null=True)
belongs_to_user = models.ForeignKey(User, related_name='user', on_delete=models.SET_NULL, null=True)
I have built a modelformset_factory for this using the following code:
personModelFormset = modelformset_factory(
person,
fields=('email', 'date' , 'phone_number', 'name'),
extra=1)
This makes the fields the form renders in the HTML email, date , phone_number, and name. This means that to successfully save the form to the database, I need to also add the fields belongs_to_group and belongs_to_user manually since the website user shouldn't be able to edit these (they should be automatically generated).
Attempted Solution:
To try to do this, I used the following view:
def classes(request):
#add form creation method here
user = request.user
group = value #taken from another form
if request.method == 'POST':
form_2 = personModelFormset (request.POST)
if form_2.is_valid():
for form in form_2:
form.belongs_to_group = value
form.belongs_to_user = user
form.save()
return redirect('home')
But this does not append the information to the form. This method works for me in a normal modelform, so I think I'm not using the modelformset_factory correctly. Does anyone know how I should correctly append the "behind the scenes" model fields to the formsetfactory? Thank you!
my form in forms.py is then passed to this method in my views.py, if I go into python shell and print objects from MyProfile, all of the fields show values except for nearbyzips, which shows None. As you can see below, I am trying to manually assign a value to nearbyzips when the form is saved.
inside views.py
#secure_required
#login_required
def profile_edit(request, username, edit_profile_form=EditProfileForm,
template_name='userena/profile_form.html', success_url=None,
extra_context=None, **kwargs):
profile = get_profile(user)
form = edit_profile_form(instance=profile, initial=user_initial)
if request.method == 'POST':
if form.is_valid()
cleanzipcode = form.cleaned_data['zipcode']
nearestzips = PostalCode.objects.distance(PostalCode.objects.get(code=cleanzipcode).location)
zip_codes = list(nearestzips.values_list('code', flat=True))
//print zip_codes
form.cleaned_data['nearbyzips'] = zip_codes
//print form.cleaned_data['nearbyzips']
profile=form.save()
return redirect(redirect_to)
models.py
class MyProfile(UserenaBaseProfile):
user = models.OneToOneField(User,
unique=True,
verbose_name=_('user'),
related_name='my_profile')
streetaddress=models.CharField(null=True, blank=True, max_length=30)
city = models.CharField(null=True, blank=True, max_length=20)
state = models.CharField(null=True, blank=True, max_length=20)
zipcode = models.IntegerField(_('zipcode'),
max_length=5, null=True, blank=True)
nearbyzips = models.IntegerField(null=True, blank=True, max_length=100)
phone=models.CharField(null=True, blank=True, max_length=16)
websiteurl=models.CharField(null=True, blank=True, max_length=38)
Something to keep in mind, if I go into python shell and run:
nearestzips = PostalCode.objects.distance(PostalCode.objects.get(code='97202').location
print nearestzips
It prints all the Postal Codes I would expect. So I'm not sure where exactly is broken. I don't see any errors in my logs.
UPDATE:
I have added print statements in my views. printing zip_codes and form.cleaned_data['nearbyzips'] both show:
[u'97202', u'97206', u'97214', u'97215', u'97239']
But it still does not appear to be saving to the form.
2 things stand out to me here.
Your form is created for some kind of profile model (get_profile_model()) -- does this profile model have a field called nearbyzips?
If your model does have a field called nearbyzips, explicitly include it (and all the fields you want to update) in a tuple/list of fields in your form class's inner Meta class.
Also, I don't see you calling the save method on your form class in your view function (i.e. form.save()).
Change this line:
tzips = PostalCode.objects.distance(PostalCode.objects.get(code='cleanzipcode').location)
to this:
tzips = PostalCode.objects.distance(PostalCode.objects.get(code=cleanzipcode).location)
I already tried nearly everything I could find regarding this, but I am pretty sure I am just one small suggestion away from solving my issue.
I am trying to save to forms that I generated using the forms method of Django at the same time. These forms have a ForeignKey relationship.
My model:
class Publisher(models.Model):
company = models.CharField(max_length=255)
address1 = models.CharField(max_length=255)
address2 = models.CharField(max_length=255)
city = models.CharField(max_length=255)
zipcode = models.CharField(max_length=10)
email = models.EmailField(max_length=255)
firstname = models.CharField(max_length=255)
lastname = models.CharField(max_length=255)
tc = models.BooleanField()
timestamp = models.DateTimeField(auto_now=True)
def __unicode__(self):
return self.company
class PublisherQuestions(models.Model):
referal = models.TextField()
updates = models.BooleanField()
publisher = models.ForeignKey(Publisher)
preferredCommunication = models.ForeignKey(PublisherCommunication)
def __unicode__(self):
return self.publisher
class PublisherForm(ModelForm):
class Meta:
model = Publisher
class PublisherQuestionsForm(ModelForm):
class Meta:
model = PublisherQuestions
exclude = ('publisher')
and my view:
def register(request):
if request.method == 'POST':
form = PublisherForm(data = request.POST)
formQuestions = PublisherQuestionsForm(data = request.POST)
if form.is_valid() and formQuestions.is_valid():
publisher = form.save()
formQuestions.publisher = publisher
formQuestions.save()
return HttpResponseRedirect('/thanks/')
So, what I try to do, is to save the second form "formQuestions" with the foreign key publisher against the publisher_id from the PublisherForm.
Unfortunately MySQL / Django is giving me the following error.
Column 'publisher_id' cannot be null
This might be a complete newbie question and yes there has been many people asking nearly the same, but none of the solutions worked for me.
Thanks for your help, as always appreciated!
You can try the following to check whether you get the same error or not:
publisher = form.save()
questions = formQuestions.save(commit=False)
questions.publisher = publisher
questions.save()
With commit=False you get the Model without saving it to the database.
https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#the-save-method
In fact it is the Model you want to manipulate, adding the Publisher and not the form.
If you need to access the data from the form I think you should use for example this:
https://docs.djangoproject.com/en/dev/ref/forms/api/#accessing-clean-data
How I understood it is that you have a ModelForm and not a Model, so if you want to manipulate the Model you first need to create it, or at least it is the cleanest way of manipulating Model data. Otherwise you can manipulate the Form data but it is another story, you want to choose the raw data or the clean data etc.
I have a model like this:
class Entity(models.Model):
entity_name = models.CharField(max_length=100)
entity_id = models.CharField(max_length=30, primary_key=True)
entity_parent = models.CharField(max_length=100, null=True)
photo_id = models.CharField(max_length=100, null=True)
username = models.CharField(max_length=100, null=True)
date_matched_on = models.CharField(max_length=100, null=True)
status = models.CharField(max_length=30, default="Checked In")
def __unicode__(self):
return self.entity_name
class Meta:
app_label = 'match'
ordering = ('entity_name','date_matched_on')
verbose_name_plural='Entities'
I also have a view like this:
def photo_match(request):
''' performs an update in the db when a user chooses a photo '''
form = EntityForm(request.POST)
form.save()
And my EntityForm looks like this:
class EntityForm(ModelForm):
class Meta:
model = Entity
My template's form returns a POST back to the view with the following values:
{u'username': [u'admin'], u'entity_parent': [u'PERSON'], u'entity_id': [u'152097'], u'photo_id': [u'2200734'], u'entity_name': [u'A.J. McLean'], u'status': [u'Checked Out'], u'date_matched_on': [u'5/20/2010 10:57 AM']}
And form.save() throws this error:
Exception in photo_match: The Entity could not be changed because the data didn't validate.
I have been trying to figure out why this is happening, but cannot pinpoint the exact problem. I can change my Entities in the admin interface just fine. If anybody has a clue about this I would greatly appreciate it!
Thanks,
Igor
If the entity you are trying to update is already saved, then you need to provide an instance parameter when you bind the form, otherwise save will try to perform an INSERT rather than an UPDATE, and the new object won't validate (check out the django docs here).
Try:
def photo_match(request):
''' performs an update in the db when a user chooses a photo '''
entity = Entity.objects.get(pk=request.POST['entity_id'])
form = EntityForm(request.POST, instance=entity)
form.save()
You'll want to be a little more robust about the way you look up the entity, of course.