So I have this DemandeForm with nested FormField:
AdressePrivee = FormField(AdresseForm, default=AdressePrivee())
with Demande model:
AdressePrivee = db.relationship(AdressePrivee, backref='Demande', lazy=False, uselist=False)
I have nested fields submitted in the HTML form:
AdressePrivee-RueEtNumero: Boulevard Bonivard 11
AdressePrivee-ComplementNom:
AdressePrivee-ComplementAdresse:
AdressePrivee-CasePostale: 01420
AdressePrivee-NPA:
AdressePrivee-Localite: Seyssel
AdressePrivee-Pays: 2
And I call it with:
form = DemandeForm(data=request.form)
if form.validate():
form.populate_obj(demande) # Here, demande.AdressePrivee should be populated
db.session.add(demande)
db.session.commit()
flash('Enregistré', 'success')
return redirect(url_for('main.index'))
But if I print it there, all entries are set to None, as if demande.AdressePrivee is only equal to AdressePrivee() empty object (log: <AdressePrivee None None None>), while FormFields should be set, with the nested fields logic, shouldn't they?
Am I missing something?
I think you can't use the constructor directly, you should use a factory.
AdressePrivee = FormField(AdresseForm, default=lambda: AdressePrivee())
I also think the attribute for transferring the form data of the request is called "formdata" not "data".
form = DemandeForm(formdata=request.form)
In the following a small example that worked for me.
class Address(db.Model):
id = db.Column(db.Integer, primary_key=True)
street = db.Column(db.String, nullable=False)
class Inquiry(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String, nullable=False)
address_id = db.Column(db.Integer,
db.ForeignKey('address.id', ondelete='cascade'),
nullable=False
)
address = db.relationship('Address', backref='inquiry', lazy=False, uselist=False)
class AddressForm(FlaskForm):
street = StringField('street', validators=[DataRequired()])
def __init__(self, csrf_enabled=False, *args, **kwargs):
super().__init__(csrf_enabled=csrf_enabled, *args, **kwargs)
class InquiryForm(FlaskForm):
title = StringField('title', validators=[DataRequired()])
address = FormField(AddressForm, default=lambda: Address())
#app.route('/new', methods=['GET', 'POST'])
def create():
item = Inquiry()
form = InquiryForm(request.form)
if form.validate_on_submit():
form.populate_obj(item)
db.session.add(item)
db.session.commit()
items = Inquiry.query.all()
return render_template('create.html', **locals())
I wish you every success in implementing your project and hope I was able to help you.
Related
I need to update my table every time a new value of "sku" is entered (not to create a new entry), but it does have to happen only if the "client" selected is the same. If the "client" is different, then the model should add a new object with the same "sku", but with different "clients".
I have tried to do the following in my models.py:
class ProductList(models.Model):
id_new = models.IntegerField(primary_key=True)
sku = models.CharField(primary_key=False, max_length=200)
client = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
name = models.CharField(max_length=256)
description = models.CharField(max_length=1000)
storage = models.CharField(max_length=256)
cost_price = models.CharField(max_length=256)
sell_price = models.CharField(max_length=256)
ncm = models.CharField(max_length=256)
inventory = models.IntegerField(null=True)
class Meta:
unique_together = (('sku', 'client'),)
But it is not working. How can I make that work?
You can try like this:
# form
class MyForm(forms.ModelForm):
class Meta:
model = ProductList
def save(self, *args, **kwargs:
client = self.cleaned_data.get('client') # get client from form cleaned_data
if hasattr(self.instance, 'pk') and self.instance.client != client: # check if client match's already existing instance's client
self.instance.pk = None # make a duplicate instance
self.instance.client = client # change the client
return super(MyForm, self).save(*args, **kwargs)
# views.py
# ...
def my_view(request, id):
instance = get_object_or_404(ProductList, id=id)
form = MyForm(request.POST or None, instance=instance)
if form.is_valid():
form.save()
return redirect('next_view')
return render(request, 'my_template.html', {'form': form})
Update
Um you can override the model as well. you can try like this:
# Untested Code but should work
def save(self, *args, **kwargs):
if self.pk:
current_instance = self.__class__.objects.get(pk=self.pk)
if current_instance.client != self.client:
self.pk = None
return super(ProductList, self).save(*args, **kwargs)
I have a flask app that connects to a MySQL DB and retrieves the data but it cannot insert anything into it. I am using flask-SQLAlchemy and the form is generated using flaskwtforms. Below is the model
class Name(db.Model):
__tablename__ = 'names'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(45), nullable=False)
password = db.Column(db.String(45), nullable=False)
def __init__(self, id, name, password):
self.id = id
self.name = name
self.password = password
def __rep__(self):
return '<User %r>' % self.username
Below is the form itself
class AddUser(FlaskForm):
name = StringField(label= 'username', validators = [InputRequired('Username is required')])
password = StringField(label = 'password', validators = [InputRequired('Password is required')])
Below is the view function
#app.route('/add_user', methods=['GET','POST'])
def add_user():
form = AddUser()
if request.method == 'POST' and form.validate_on_submit():
name = form.name.data
password = form.password.data
name = Name(name,password)
db.session.add(name)
db.session.commit()
return render_template('add-user.html')
else:
return render_template('add-user.html', form=form)
I found the solution to my problem. So in my database, the id is set as the primary key and is auto-generated hence it does not need to be passed in the constructor since my query would also require me to pass an id.
I have two tables in one-to-many relationship, table Project has many Reports.
While the user entering a report issue, s/he will have to choose the project that this report belong to, from a drop-down list.
The drop-down list shows the projects name but the add_report() stops at db.session.commit() and when I print the _form.project_list.data_, I get the project name instead of the foreign key.
I believe my problem is in the forms, I tried many codes to get the project_id but I got the "unprintable InterfaceError object" error.
My Question:
How can I get the project id number instead of the project name from the drop-down list?
views.py
#app.route('/add_report/', methods=['GET', 'POST'])
def add_report():
form = AddReportForm(request.form)
if request.method == 'POST':
if form.validate_on_submit():
new_report = Report(
project_id=form.project_list.data,
issue=form.issue.data)
db.session.add(new_report)
db.session.commit()
flash('New report was successfully added.')
return redirect(url_for('projects'))
else:
flash('All fields are required.')
return redirect(url_for('projects'))
return render_template('project.html', form=form)
Models.py
class Project(db.Model):
project_id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
date = db.Column(db.Date)
reports = db.relationship('Report', backref='project', lazy='dynamic')
def __repr__(self):
return self.name
class Report(db.Model):
report_id = db.Column(db.Integer, primary_key=True)
project_id = db.Column(db.Integer, db.ForeignKey('project.project_id'))
issue = db.Column(db.Text)
def __repr__(self):
return self.issue
forms.py
def get_projects():
return Project.query
class AddReportForm(Form):
project_list = QuerySelectField('Project', query_factory=get_projects)
issue = StringField('issue')
Thank you very much
Report table? do you mean the dropdown list? did you try
project_list = QuerySelectField('Project', query_factory=get_projects, get_label='project_id')
Check this question to reduce your query to only select the required columns.
Finally I find what was wrong with my code :)
I was using project_id(Foreign Key) in the report table to obtain the project id(form.project_list.data) instead of projects(relationship). So I made some changes in models.py and views.py.
Poor forms.py, I thought it was your fault!
models.py
class Project(db.Model):
project_id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
date = db.Column(db.Date)
def __repr__(self):
return self.name
class Report(db.Model):
report_id = db.Column(db.Integer, primary_key=True)
project_id = db.Column(db.Integer, db.ForeignKey('project.project_id'))
projects = db.relationship('Project', backref='report')
issue = db.Column(db.Text)
def __repr__(self):
return self.issue
views.py
#app.route('/add_report/', methods=['GET', 'POST'])
def add_report():
form = AddReportForm(request.form)
if request.method == 'POST':
if form.validate_on_submit():
new_report = Report(
projects=form.project_list.data,
issue=form.issue.data)
db.session.add(new_report)
db.session.commit()
flash('New report was successfully added.')
return redirect(url_for('projects'))
else:
flash('All fields are required.')
return redirect(url_for('projects'))
return render_template('project.html', form=form)
forms.py stays the same
I'm having trouble saving data for an intermediate field. I have forms (unlisted here) to create Stocks and Portfolios. Now I am trying to build a Membership form to link the two. I followed the django documentation and the code works perfectly in the the API. Then in the views, I can the membership to save fine (I pulled it up in the API afterwards to check), but it doesn't make the make the connection between the Stock and Portfolio. After the membership form, there is a membership object saved in my database but it is not connecting the portfolio to the stock.
Thanks in advance.
Here are my models:
class Stock(models.Model):
name = models.CharField(max_length=160)
ticker = models.SlugField(max_length=20)
created = models.DateTimeField(auto_now_add=True, blank=True)
class Portfolio(models.Model):
name = models.CharField(max_length=160)
stocks = models.ManyToManyField(Stock, through='Membership')
class Membership(models.Model):
stock = models.ForeignKey(Stock)
portfolio = models.ForeignKey(Portfolio)
shares = models.IntegerField(default=0)
class StockForm(forms.ModelForm):
class Meta:
model = Stock
exclude = []
class PortfolioForm(forms.ModelForm):
class Meta:
model = Portfolio
exclude = []
class MembershipForm(forms.ModelForm):
class Meta:
model = Membership
exclude = []
And here are my views.py:
def membership_form(request):
if request.method == 'POST':
form = MembershipForm(request.POST, request.FILES)
if form.is_valid():
new_obj = form.save(commit=False)
new_obj.save()
membership = Membership.objects.create( portfolio=new_obj.portfolio ,stock=new_obj.stock)
membership.save()
return HttpResponseRedirect(reverse('portfolios', args=[]))
else:
form = MembershipForm(None)
return render(request, 'portfolio/form.html', {'form': form})
I based the views off Django m2m form save " through " table .
Write a custom form and save the relationship inside it.
I have taken the example as classes of of email address associated with an email that i wanted to send
class SendForm (forms.ModelForm) :
To = forms.ModelMultipleChoiceField(
queryset=EmailId.objects.filter(),required=False,widget=FilteredSelectMultiple(('To'), False))
def __init__(self, *args, **kwargs):
initial = kwargs.setdefault('initial', {})
if 'instance' in kwargs:
initial['To']=kwargs['instance'].To.all()
super(SendForm, self).__init__(*args, **kwargs)
def save(self, commit=True):
instance = forms.ModelForm.save(self, commit)
instance.save(send=0)
es = [e for es in self.cleaned_data['To']]
for relation in instance.To.all():
instance.To.remove(relation)
for e in es:
instance.To.add(e)
instance.save(send=1)
return instance
class Meta:
model =SentEmail
I have a model with a custom property
class RecipeIngredient(models.Model):
recipe = models.ForeignKey(Recipe)
ingredient = models.ForeignKey(Ingredient)
serving_size = models.ForeignKey(ServingSize)
quantity = models.IntegerField()
order = models.IntegerField()
created = models.DateTimeField(auto_now_add = True)
updated = models.DateTimeField(auto_now = True)
def _get_json_data(self):
return u'%s %s' % (self.id, self.ingredient.name)
json_data = property(_get_json_data)
I am trying to display the property 'json_data' in my template.
I have this piece of code in my form
class RecipeIngredientForm(forms.ModelForm):
json_data = forms.CharField(widget=forms.HiddenInput())
def __init__(self, *args, **kwargs):
super(RecipeIngredientForm, self).__init__(*args, **kwargs)
print('here')
if kwargs.has_key('instance'):
instance = kwargs['instance']
print(instance)
self.initial['json_data'] = instance.json_data
I know the data in my property 'json_data' is not valid, but I am unable to see the data from this property in my template.
In my views, I have this piece of code
RecipeIngredientFormSet = inlineformset_factory(models.Recipe, models.RecipeIngredient, form=forms.RecipeIngredientForm, extra=0)
recipe_id = int(id)
objRecipe = models.Recipe.objects.get(id=recipe_id)
recipe = forms.RecipeForm(instance=objRecipe)
recipeIngredients = RecipeIngredientFormSet(instance = objRecipe)
I guess my question is how do I display data from an extra model field?
pass the data of the extra fields as initial data to the form
initial_data = [{'json_data': objRecipe.json_data}]
recipeIngredients = RecipeIngredientFormSet(initial=initial_data, instance = objRecipe)
for correct usage of initial data refer using initial data with formset