Unable to edit the django table - python

Unable to perform the update operation.
This function is for table update, but the code is not allowing me to pass the instance in the form because it is not a model form. Please suggest the changes.
class userForm(forms.Form):
SHIFT_CHOICES = ( ('D','DAY'), ('N','NIGHT') )
ADMISSION_FORM_STATUS = ( ('Y','YES'), ('N','NO') )
FORM_COMPLETE_STATUS = ( ('Y','YES'), ('N','NO'))
TRAINING_STATUS = ( ('Y','YES'), ('N','NO') )
STATUS = ( ('W','WORKING'), ('OL','ON_LEAVE'), ('E','EXIT') )
employee_id = forms.CharField(max_length=8,required=False)
employer_id = CompanyModelChoiceField(required=False, queryset=Company.objects.all(), label='Employer', widget=Select2Widget)
name = forms.CharField(max_length=255)
uber_name = forms.CharField(max_length=255, required=False)
mobile = forms.CharField(max_length=20, required=False)

To bind data with the form fields, (not a model form), you will have to pass a dictionary with those specific fields to the form constructor.
In this case what you need to do is
Get the form data in a dictionary
driver = Driver.objects.get(employee_id = employee_id)
form_data = {'employee_id':driver.employee_id, 'employer_id', driver.employer_id, .., .., .} #all the fields in the form
AddDriverForm(initial=form_data)
Then while saving the data you should do it the same way you did for creating the driver.
Moreover, I would personally suggest you to use Model form for this case since you are going to put that data in the model anyways, it will surely save you the hassle.
Also, you might consider to normalize your model if its not a big deal.

Lets say your model (DC) has an instance (an entry):
Superhero = Bruce, Butler = Alfred, Engineer = Lucius, car = Batmobile
Your form has
Superhero, Butler, Engineer
(Notice that my form is missing the car field)
Now, suppose you want to change the name Butler to Jarvis
instance = DC.objects.get(id=8) #lets consider this as the id of the instance we have
#what you need to do is save the required data in a dictionary in this case
form_data = {'Superhero':instance.Superhero, 'Butler':instance.Butler, 'Engineer': instance.Engineer } #we haven't passed the car field
#Also I think that passing extra fields must not be an issue but I haven't tried
UpdateForm(form_data)
This will render the data properly when the form is displayed.

Related

Best way to add/alter fields in a form

I have the following model
class customer_prices(models.Model):
user = models.ForeignKey(User, on_delete = models.CASCADE,null=True)
price = models.FloatField()
image = models.FloatField()
function = models.TexField()
.
.
(10 more fields)
The fields shown in the homepage is only price thus the other fields are assigned manually, based on the values in a dataframe returned by a self-written some_util_function() e.g
#login_required
def add_price(request):
cleaned_data = form.cleaned_data
df = some_util_function(cleaned_data["price"])
form.user_id = user.id
form.image= df["image"]
.
.
form.save()
messages.success(request, "Price added")
return redirect("my_html")
return render(...)
As we can see each form-field is updated by form.field=df["field"] thus I wonder, if there is another (better way) of doing that?
I have thought about the two following ways:
Instead of form.field=value then form.cleaned_data["field"] = value
Since I have all the data in a dataframe, simply push that dataframe to the given DB e.g df.to_sql("my_table_name").
I know (2) is working, but I think it's better to keep the database changes to Django, and not some mix between pushing data self and let Django handle it.

Why is my WTForm HiddenField not being set to a default value?

I have an EmployeeForm with one of the fields comprising of a PhotoForm shown below:
class EmployeeForm(Form):
name = StringField('Full Name', validators=[DataRequired()])
title = StringField('Job Title', validators=[DataRequired()])
email = StringField('Company Email', validators=[DataRequired()])
department = StringField('Department', validators=[DataRequired()])
photo = FormField(PhotoForm)
Depending on whether a USE_S3 config variable is set to True or False I want two fields on my photo object to be automatically set to default values like so:
class PhotoForm(Form):
image = S3ImageUploadField(base_path=app.config['UPLOAD_FOLDER'],namegen=photo_name_generator)
if app.config['USE_S3']:
image_storage_type = HiddenField('s3')
image_storage_bucket_name = HiddenField(app.config['S3_BUCKET_NAME'])
else:
image_storage_type = HiddenField('empty string')
image_storage_bucket_name = HiddenField('empty string')
However, when I check the form's html there is no hidden field nor when I check the database on submission is there any set values on the image_storage_type and image_bucket_name and I can't figure it out why that is. I've checked on some stackoverflow questions but they don't exactly fit my question because I am using Flask-Admin and WTForm together which in my opinion makes it a little more trickier.
Also, my admin.py file for the form rendering looks like this:
class EmployeeView(ModelView):
form = EmployeeForm
I'm not sure if this is a problem on WTForm's side or Flask-Admin's side but I've been trying for a long time to make this work. Does anyone has any ideas? Help would be greatly appreciated.
The first argument for a field is its label. In your code you aren't passing data into the field, you are setting its label. Set the default data with the default kwarg.
from wtforms import Form, HiddenField
class PhotoForm(Form):
default_image_data = "foo" if True else "bar"
image_storage_type = HiddenField('image storage', default=default_image_data)
>>> form = PhotoForm()
>>> form.image_storage_type.data
'foo'
>>> form.image_storage_type()
u'<input id="image_storage_type" name="image_storage_type" type="hidden" value="foo">'

Dynamic Fields ModelForms Django

I am trying to figure out how to dynamically change a ModelForm field based on the input from a previous field.
For example, if I have these kinds of models:
class Phone(models.Model):
name = models.CharField(max_length=10)
class Series(models.Model):
name = models.CharField(max_length=10)
class Manufacturer(models.Model):
phone = models.ForeignKey('Phone')
series = models.ForeignKey('Series')
class ManufacturerForm(ModelForm):
class Meta:
model = Manufacturer
Which would generate a form (ManufacturerForm) with dropdown options for the phone and series entries in the database. Is it possible to a different set of series entries based on the phone entered by the user, for example?
I have read about using the __init__ method to accomplish this, based on what I have read on this blog post, but I am not sure how to execute this given my scenario. Or maybe there is a better way to go about achieving this that you have taken? Thanks for any advice!
EDIT: Added the form's view.
def make_ad(request):
if request.method == 'POST':
form = ManufacturerForm(request.POST, request.FILES)
if form.is_valid():
a = form.save()
a.user = request.user
a.save()
else:
form = ManufacturerForm()
variables = RequestContext(request, {
'form': form
})
return render_to_response('place.html', variables)
#super9 mentioned using ajax request to change these elements. I need to check if request.is_ajax(), but at what point should I check this in my view? And how do I add or change the queryset based on the ajax request?
Thanks for your advice.
EDIT: Trying to use django-smart-selects
Not sure how to setup my models to utilize django-smart-selects to accomplish what I am trying. Here is how I have structured my models:
from smart_selects.db_fields import ChainedForeignKey
class Phone(models.Model):
phone = models.CharField(max_length=10)
class Series(models.Model):
series = models.CharField(max_length=10)
phone = models.ForeignKey(Phone)
class SeriesModel(models.Model):
model = models.CharField(max_length=10)
series = models.ForeignKey(Series)
class Manufacturer(models.Model):
phone = models.ForeignKey(Phone)
series = ChainedForeignKey (Series, chained_field = "phone", chained_model_field = "phone")
series_model = ChainedForeignKey (SeriesModel, chained_field = "series", chained_model_field = "series")
But when I view my form (ModelForm) the fields for series_model are not chained properly to series. Am I missing something to make smart-selects work on the second layer of abstraction?
EDIT: Above code now works.

How to dynamically create a ModelForm based on James Bennett's article "So you want a dynamic form"?

In James Bennett's article "So you want a dynamic form" (Nov. 9, 2008), he wrote that to create a dynamic form you can do something like this:
def make_contact_form(user):
fields = { 'name': forms.CharField(max_length=50),
'email': forms.EmailField(),
'message': forms.CharField(widget=forms.Textarea) }
if not user.is_authenticated():
fields['captcha'] = CaptchaField()
return type('ContactForm', (forms.BaseForm,), { 'base_fields': fields })
But how would you do the same thing with forms.ModelForm?
So far I'm just doing something like this (I couldn't figure out how to use type with an inner class 'Meta')
def make_order_edit_form(include_fields):
class _OrderEditForm(forms.ModelForm):
if 'fa_date' in include_fields:
fa_date = CustomDateTimeField(label="first appointment time")
class Meta:
model = Order
fields = include_fields
widgets = custom_widgets
return _OrderEditForm
where include_fields is a tuple of fields I want to show.
However even if I wrote a correct make_order_edit_form, how do I use it in views.py? Specifically how do I pass both the POST request and the order instance to it? Normally I would do something like
order = Order.objects.get(pk=pk)
order_form = OrderEditForm(data=request.POST, instance=order)
Bonus question:
Why did Bennett create a ContactForm out of forms.BaseForm instead of forms.Form? (I'm assuming that's why the fields are called base_fields as well.)
make_order_edit_form returns a ModelForm class, thus you could
form_cls = make_order_edit_form(fields)
order_form = form_cls(request.POST, instance=order)
For the bonus question, check the Form code:
class Form(BaseForm):
"A collection of Fields, plus their associated data."
# This is a separate class from BaseForm in order to abstract the way
# self.fields is specified. This class (Form) is the one that does the
# fancy metaclass stuff purely for the semantic sugar -- it allows one
# to define a form using declarative syntax.
# BaseForm itself has no way of designating self.fields.
__metaclass__ = DeclarativeFieldsMetaclass
The Form has a customized meta class DeclarativeFieldsMetaclass which automatically collects form fields written in declarative syntax, if you use Form in type(), it looks like (take Bennett's example)
type('ContactForm', (forms.Form,), {
'name': forms.CharField(max_length=50),
'email': forms.EmailField(),
'message': forms.CharField(widget=forms.Textarea)}
# instead of
type('ContactForm', (forms.BaseForm,), { 'base_fields': fields })
update
to build up ModelForm using type, no much different
def make_order_edit_form(include_fields):
d = {}
class Meta:
model = Order
fields = include_fields
widgets = custom_widgets
d = {'Meta':Meta}
if 'fa_date' in include_fields:
d['fa_date'] = CustomDateTimeField(label="first appointment time")
return type('OrderEditForm', (forms.ModelForm,), d)

How Can I Populate Default Form Data with a ManyToMany Field?

Ok, I've been crawling google and Django documentation for over 2 hours now (as well as the IRC channel on freenode), and haven't been able to figure this one out.
Basically, I have a model called Room, which is displayed below:
class Room(models.Model):
"""
A `Partyline` room. Rooms on the `Partyline`s are like mini-chatrooms. Each
room has a variable amount of `Caller`s, and usually a moderator of some
sort. Each `Partyline` has many rooms, and it is common for `Caller`s to
join multiple rooms over the duration of their call.
"""
LIVE = 0
PRIVATE = 1
ONE_ON_ONE = 2
UNCENSORED = 3
BULLETIN_BOARD = 4
CHILL = 5
PHONE_BOOTH = 6
TYPE_CHOICES = (
('LR', 'Live Room'),
('PR', 'Private Room'),
('UR', 'Uncensored Room'),
)
type = models.CharField('Room Type', max_length=2, choices=TYPE_CHOICES)
number = models.IntegerField('Room Number')
partyline = models.ForeignKey(Partyline)
owner = models.ForeignKey(User, blank=True, null=True)
bans = models.ManyToManyField(Caller, blank=True, null=True)
def __unicode__(self):
return "%s - %s %d" % (self.partyline.name, self.type, self.number)
I've also got a forms.py which has the following ModelForm to represent my Room model:
from django.forms import ModelForm
from partyline_portal.rooms.models import Room
class RoomForm(ModelForm):
class Meta:
model = Room
I'm creating a view which allows administrators to edit a given Room object. Here's my view (so far):
def edit_room(request, id=None):
"""
Edit various attributes of a specific `Room`. Room owners do not have
access to this page. They cannot edit the attributes of the `Room`(s) that
they control.
"""
room = get_object_or_404(Room, id=id)
if not room.is_owner(request.user):
return HttpResponseForbidden('Forbidden.')
if is_user_type(request.user, ['admin']):
form_type = RoomForm
elif is_user_type(request.user, ['lm']):
form_type = LineManagerEditRoomForm
elif is_user_type(request.user, ['lo']):
form_type = LineOwnerEditRoomForm
if request.method == 'POST':
form = form_type(request.POST, instance=room)
if form.is_valid():
if 'owner' in form.cleaned_data:
room.owner = form.cleaned_data['owner']
room.save()
else:
defaults = {'type': room.type, 'number': room.number, 'partyline': room.partyline.id}
if room.owner:
defaults['owner'] = room.owner.id
if room.bans:
defaults['bans'] = room.bans.all() ### this does not work properly!
form = form_type(defaults, instance=room)
variables = RequestContext(request, {'form': form, 'room': room})
return render_to_response('portal/rooms/edit.html', variables)
Now, this view works fine when I view the page. It shows all of the form attributes, and all of the default values are filled in (when users do a GET)... EXCEPT for the default values for the ManyToMany field 'bans'.
Basically, if an admins clicks on a Room object to edit, the page they go to will show all of the Rooms default values except for the 'bans'. No matter what I do, I can't find a way to get Django to display the currently 'banned users' for the Room object. Here is the line of code that needs to be changed (from the view):
defaults = {'type': room.type, 'number': room.number, 'partyline': room.partyline.id}
if room.owner:
defaults['owner'] = room.owner.id
if room.bans:
defaults['bans'] = room.bans.all() ### this does not work properly!
There must be some other syntax I have to use to specify the default value for the 'bans' field. I've really been pulling my hair out on this one, and would definitely appreciate some help.
Thanks!
UPDATE
lazerscience actually helped me find the solution in one of his comments. Basically, the way it works is if you pass a list of primary keys. To make it work I had to change:
if room.bans:
defaults['bans'] = room.bans.all() ### this does not work properly!
to
if room.bans:
defaults['bans'] = [b.pk for b in room.bans.all()]
And bam, it instantly started working (when I view the page, it will show a selectable list of Callers, with the already banned callers already highlighted (selected).
You probably need to use "initial": Django set default form values

Categories