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
Related
I am just beginning with python and flask and am looking to create an app. The app allows users to enter their favourite artists in one page. In the next page then allows the user to enter favourite tracks using SelectField where the artist names are given as the options.
I have the following
models:
from application import db
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, BooleanField, IntegerField, SelectField
class Artist(db.Model):
id = db.Column(db.Integer, primary_key=True)
artist_name = db.Column(db.String(45), nullable=False)
music_artist = db.relationship('Music', backref='musicbr')
def __repr__(self):
return 'Choose {}'.format(self.artist_name)
class Music(db.Model):
id = db.Column(db.Integer, primary_key=True)
track_name = db.Column(db.String(45), nullable=False)
artist_id = db.Column(db.Integer, db.ForeignKey('artist.id'), nullable=False)
class AddArtistForm(FlaskForm):
artist_name = StringField('Artist Name')
submit = SubmitField('Add Artist!')
class AddMusicForm(FlaskForm):
track_name = StringField('Track Name')
artist_name = SelectField('Artist Name', coerce=int)
submit = SubmitField('Add Track!')enter code here
and the following routes
#app.route('/')
def home():
return render_template('index.html')
#app.route('/add_artist', methods = ['GET', 'POST'])
def add_artist():
form = AddArtistForm()
if form.validate_on_submit():
new_artist = Artist(artist_name =form.artist_name.data)
db.session.add(new_artist)
db.session.commit()
return render_template('index.html', message="Artist Added!")
else:
return render_template('add_artist.html', form=form)
#app.route('/add_music', methods = ['GET', 'POST'])
def add_music():
form = AddMusicForm()
if form.validate_on_submit():
new_track = Music(track_name =form.track_name.data)
artist_choice = Artist.query.all(artist_name=form.artist_name.data)
db.session.add(new_track)
db.session.commit()
return render_template('index.html', message="Track Added!")
else:
return render_template('add_music.html', form=form)
Is someone able to help me to understand what code I need to implement here?
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.
I have a userprofile that captures the username and the group the user is assigned to. I want the uploaded files to be saved under the group name folder. The folders already exit at the media root, the files shoud be routed to these folder
I solved the problem by the solution given. Now the username is shown as a dropdown list on the upload page. I want only the logged it username to be shown or exclude even showing it
models.py
class uploadmeta(models.Model):
path = models.ForeignKey(Metadataform, on_delete=models.CASCADE)
user_profile = models.ForeignKey(UserProfile, on_delete=models.CASCADE, null=True, verbose_name='Username')
tar_gif = models.FileField(upload_to=nice_user_folder_upload, verbose_name="Dataset") # validators=[FileExtensionValidator(allowed_extensions=['tar', 'zip'])]
def __str__(self):
return self.request.user.username
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
Group= models.CharField(max_length=500, choices=Group_choices, default='Please Select')
def __str__(self):
return self.user.username
view.py
def uploaddata(request):
if request.user.is_authenticated:
if request.method == 'POST':
form = uploadmetaform(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('file_list')
else:
form = uploadmetaform()
return render(request, 'uploaddata.html', {
'form': form
})
else:
return render(request, 'home.html')
forms.py
class uploadmetaform(forms.ModelForm):
count = Metadataform.objects.all().latest('id').id #To know the id of latest object
data = Metadataform.objects.all().filter(id=count) #return the queryset with only latest object
path = forms.ModelChoiceField(queryset=data)
def __init__(self, *args, **kwargs):
super(uploadmetaform, self).__init__(*args, **kwargs)
count = Metadataform.objects.all().latest('id').id
data = Metadataform.objects.all().filter(id=count)
self.fields['path'] = forms.ModelChoiceField(queryset=data)
class Meta:
model = uploadmeta
fields = ['path', 'user_profile','tar_gif',]
You can use the upload_to argument in the FileField.
It accept a string representing the path where you want to store the file or you can pass in a function which let you add more details.
More info from the doc: https://docs.djangoproject.com/fr/2.2/ref/models/fields/#django.db.models.FileField.upload_to
You may need to add a foreign key form uploadmeta to UserProfile like :
user_profile = models.ForeignKey(UserProfile, on_delete=models.PROTECT)
Then you can use the following
def nice_user_folder_upload(instance, filename):
extension = filename.split(".")[-1]
return (
f"your_already_definied_folder/{instance.user_profile.group}/{file}.{extension}"
)
Then use it in uploadmeta FileField
doc = models.FileField(upload_to=nice_user_folder_upload, verbose_name="Dataset")
Hello All I have been trying for days to solve this issue, however I am really not sure where my error is. I am new to django and have been coding for a year.
I have a model portfolio that takes in tickers and also linked to the user via a foreign key. In my models
class Portfolio(models.Model):
ticker = models.CharField(max_length=15)
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='investor', null=True)
def __str__(self):
return self.ticker
my form is as such:
class PortfolioForm(forms.ModelForm):
class Meta():
model = Portfolio
fields = ['ticker']
def __init__(self, user, *args, **kwargs):
super(PortfolioForm, self).__init__(*args, **kwargs)
self.fields['ticker'].queryset = Portfolio.objects.filter(user=user)
and in my views:
if request.method == 'POST':
form = PortfolioForm(request.POST or None, request.user)
if form.is_valid():
ticker = form.save(commit=False)
ticker.user = request.user
ticker.save()
return redirect('/add_stock')
else:
ticker = Portfolio.objects.filter(pk = request.user.id)
output = []
for ticker_item in ticker:
output.append(str(ticker))
return render(request, 'test_app/add_stock.html', {'ticker':ticker, 'output':output})
I want the user to add stocks to the Portfolio database but only return the stock tickers they added. Currently the stocks are being added to the database but nothing is returned to the user. Also stocks added to the database are available for any user to see not just the specific logged in user. I have added #login_required to the top of the specific view. I'd appreciate any help with this issue. Thank you all.
Change this
ticker = Portfolio.objects.filter(pk = request.user.id)
to
ticker = Portfolio.objects.filter(user = request.user)
This should return all tickers added by the current user.
I have a ModelChoiceField called outage_name. I also have a simple form that allows you to select the item from the list. The ModelChoiceField is pulled from a MySQL DB. This queryset is located in forms.py
outage_name = forms.ModelChoiceField(queryset = Outage.objects.filter(published = True)
The models.py is listed below.
from django.db import models
from django.contrib.auth.models import User
class Outage(models.Model):
outage_name = models.CharField(max_length=60, unique=True)
published = models.BooleanField()
def __unicode__(self):
return self.outage_name
class Detail(models.Model):
detail = models.CharField(max_length=60, unique=True)
user = models.ForeignKey(User)
outage = models.ForeignKey(Outage)
def __unicode__(self):
return self.outage
When I select from the list and submit the form I can't seem to figure out how to match outage = models.ForeignKey(Outage) that was selected on the list. To the correct outage_name. In my views.py I can hard code the id and it submits to the database and everything works fine.
def turnover_form(request):
if request.user.is_authenticated():
if request.method == 'POST':
form = TurnoverForm(request.POST)
if form.is_valid():
details = Detail.objects.get_or_create(
detail = form.cleaned_data['detail'],
user = request.user,
outage = Outage.objects.get(pk=1))
return HttpResponseRedirect('/turnover/user/')
else:
form = TurnoverForm()
variables = RequestContext(request, {'form': form})
return render_to_response('turnover_form.html', variables)
else:
return HttpResponseRedirect('/authorization/')
Any advice on how to match the id with the selected item would be appreciated. I'm sure my code is not very pythonic as I'm still learning.
outage = form.cleaned_data['outage'] # cleaned_data['outage'] is a model instance