Form update and display data in same page Flask project - python-2.7

I am getting confused on how to implement this. Basically, I have edit_profile page where after login, user gets redirected to this page to fill in his profile. User can fill in his data, click save and then it saves the data in MySQL and comes back to the same edit_profile with all the fields fill in from database.
#app.route("/edit_profile", methods=['GET','POST'])
#login_required
def edit_profile():
custprofileform = CustProfileForm()
if request.method == "POST" and custprofileform.validate_on_submit():
get_user_profile = CustProfileTB.query.filter_by(cust_id = current_user.id).first()
if get_user_profile is None:
profiledata = CustProfileTB(first_name=custprofileform.first_name.data,
last_name= custprofileform.last_name.data,
first_line_address=custprofileform.first_line_address.data,
second_line_address=custprofileform.second_line_address.data,
postcode=custprofileform.phone.data,
phone=custprofileform.phone.data,
agree_tos=custprofileform.agree_tos.data,
cust_id=current_user.id)
db.session.add(profiledata)
db.session.commit()
flash("Customer profile saved successfully")
return render_template("edit_profile.html", first_name=custprofileform.first_name.data)
else:
#profile already exists, so update fileds
profiledata = CustProfileTB(obj=get_user_profile)
db.session.commit()
return render_template("edit_profile.html", first_name=custprofileform.first_name.data)
if request.method == 'GET':
data_to_populate = CustProfileTB.query.filter_by(cust_id=current_user.id).first()
if data_to_populate:
return render_template("edit_profile.html", custprofileform=data_to_populate)
return render_template("edit_profile.html", custprofileform=custprofileform)
my question is, is this correct way to do it?
At the moment, data gets saved in SQLite, but then the form is shown, I see empty textboxes.
Am I passing in the values correctly after form save?
How do I pass values after save?
Can I show it as simple as {{ first_name }}
my Model is :
class CustProfileTB(db.Model):
id = db.Column(db.Integer(), primary_key=True)
first_name = db.Column(db.String(50))
last_name = db.Column(db.String(50))
first_line_address = db.Column(db.String(255))
second_line_address = db.Column(db.String(255))
postcode = db.Column(db.String(255))
phone = db.Column(db.String(20))
agree_tos = db.Column(db.Boolean(), default=False)
cust_id = db.Column(db.Integer())
def __init__(self, first_name=None, last_name=None, first_line_address=None, second_line_address=None, postcode=None, phone=None, agree_tos=None, cust_id=None ):
self.first_name = first_name
self.last_name = last_name
self.first_line_address = first_line_address
self.second_line_address = second_line_address
self.postcode = postcode
self.phone = phone
self.agree_tos = agree_tos
self.cust_id = cust_id
def __repr__(self):
self.res = self.first_name+" "+self.last_name
return '<CustProfileTB {self.res}>'.format(self=self)
When I query from sqlite like :
CustProfileTB.query.filter_by(cust_id=4).first()
I just get what I mentioned in repr which is first_name and last_name. How do I get all the fields and how do I display this data in the form...?
and this does not work:
<label class="col-lg-3 control-label">First Name</label>
<div class="col-lg-8">
{{ custprofileform.csrf_token }}
{% if not first_name %}
<input type="text" name="first_name" placeholder="Insert First Name" data-required="true" class="form-control">
{% else %}
<input type="text" name="{{ first_name }}" placeholder="Insert First Name" data-required="true" class="form-control">
{% endif %}
</div>

Related

How to list only active users in dropdown menu

I want to list all active users in dropdown list. But all users are listed in the dropdown
How can I do it?
template
<form method="post">
{% csrf_token %}
{{ form|crispy }}
<input type="hidden" name="customer_id" value="{{ customer.id }}">
<button type="submit" class="btn btn-primary btn-sm">Assign</button>
</form>
forms
class AssignForm(forms.ModelForm):
class Meta:
model = Customer
fields = ('user',)
views
form = AssignForm(request.POST or None)
if request.POST:
customer_id = request.POST.get('customer_id', None)
customer = Customer.objects.get(id=customer_id)
user = UserProfile.objects.get(id=request.POST.get('user', None))
customer.user = user
customer.save()
# form.save()
return redirect('user:customer_list')
models
class UserProfile(AbstractUser, UserMixin):
company = models.ForeignKey(CompanyProfile, on_delete=models.CASCADE, null=True, unique=False)
username = models.CharField(max_length=500, unique=True)
first_name = models.CharField(max_length=200)
...
You can filter the user field in the assignForm with:
class AssignForm(forms.ModelForm):
user = forms.ModelChoiceField(
queryset=UserProfile.objects.filter(is_active=True)
)
class Meta:
model = Customer
fields = ('user',)
You can also filter this already at the model level with:
class Customer(models.Model):
user = models.OneToOneField(
'UserProfile',
limit_choices_to={'is_active': True}
)
In that case you do not need to specify this in the ModelForm(s).
I would strongly advise that you use the form in a POST request to create an instance, so:
customer_id = request.POST.get('customer_id')
if customer_id:
instance = get_object_or_404(Customer, id=customer_id)
else:
instance = None
if request.method == 'POST':
form = AssignForm(request.POST, instance=instance)
if form.is_valid():
form.save()
return redirect('user:customer_list')
# …
You can create a new model ActiveUser and impose a filter to it (see https://www.django-rest-framework.org/api-guide/filtering/), depending on what "active" means in your case.

How to query a user's display information in the AccountDisplayInformation from the AccountModel

Account is my AUTH_USER_MODEL and AccountDisplayInfo consists of all the additional display info of every account. So they can input and submit, and subsequently update their information. These are my codes, but I'm unsure why it isn't working. First of all, I am receiving this error:
DoesNotExist at /account/5/displayinfo/ AccountDisplayInfo matching query does not exist.
Secondly, the "update" function isn't working.
models.py
class Account(AbstractBaseUser):
email = models.EmailField(verbose_name="email", max_length=60, unique=True)
username = models.CharField(max_length=30, unique=True)
class AccountDisplayInfo(models.Model):
account = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
instagram = models.CharField(max_length=50, unique=True, blank=True, null=True) #instagram
.html
<form method="POST" enctype="multipart/form-data">{% csrf_token %}
{{ form.as_p }}
<div class="d-flex justify-content-center">
<button type="submit" class="btn btn-primary btn-sm col-lg-5">Update</button>
</div>
</form>
views.py
def display_information_view(request, *args, **kwargs):
user_id = kwargs.get("user_id")
account = Account.objects.get(pk=user_id)
context = {}
displayinfo = AccountDisplayInfo.objects.get(account=account)
if request.POST:
form = DisplayInformationForm(request.POST, request.FILES, instance=request.user)
if form.is_valid():
info = form.save(commit=False)
info.account = request.user
info.save()
messages.success(request, 'Your profile display information have been updated', extra_tags='editdisplayinfo')
return redirect("account:view", user_id=account.pk)
else:
form = DisplayInformationForm(request.POST, instance=request.user,
initial={
"instagram": displayinfo.instagram,
}
)
context['form'] = form
else:
form = DisplayInformationForm(
initial={
"instagram": displayinfo.instagram,
}
)
context['form'] = form
return render(request, "account/displayinfo.html", context)
forms.py
class DisplayInformationForm(forms.ModelForm):
class Meta:
model = AccountDisplayInfo
fields = ('instagram')
Also, would be great if you can advise on this::
If I have 2 tables. Table 1 and Table 2. Table 2 has a foreign key to table 1 but table 1 dont have a foreign key to table 2. How can I query table 2's data from table 1? Thanks
By default .get() will return a DoesNotExist exception if no object matches the query you executed and stop the code from running, so if you want to input it manually on the same page use filter instead:
displayinfo = AccountDisplayInfo.objects.filter(account=account).first()
Then in your template do something like this:
{% if displayinfo %}
... show display info...
{% else %}
<p> No info yet </p> <!-- (or show some form) -->
{% endif %}
To answer your other question:
You have to use the related_name or related models attribute to access the ForeignKey data or use the model name with the _set suffix, for example:
class Post(models.Model):
title = models.CharField(max_lenght=10)
class Comment(models.Model):
body = models.CharField(max_lenght=200)
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
then you would get the Post and its comments:
post = Post.objects.get(pk=1)
comments = post.comments.all()
if you didn't have the related_name attribute in your model field you would do this instead:
comments = post.comment_set.all()
UPDATE
Maybe the issue is in your Form class, try removing the save method from it and instead do this in your view:
if request.POST:
form = DisplayInformationForm(request.POST, request.FILES, instance=request.user)
if form.is_valid():
info = form.save(commit=False)
info.account = request.user
messages.success(request, 'Your profile display information have been updated', extra_tags='editdisplayinfo')
info.save()
return redirect("account:view", user_id=account.pk)

How to fix modelformset error: __init__() missing 1 required positional argument: 'user'

This is my first attempt at using formsets and I am stuck with this error. Where am I going wrong? I don't fully understand how this works yet. I think my form is expecting a user but I don't know what to do with it. Thanks for any help!
error:
init() missing 1 required positional argument: 'user'
model:
class PropertySubmission(models.Model):
BANNER_CHOICES = (
('NB', 'No Banner'),
('FL', 'For Lease'),
('FS', 'For Sale'),
('NL', 'New Listing'),
('SD', 'Sold'),
('LD', 'Leased'),
('RD', 'Reduced'),
('NP', 'New Price'),
('SC', 'Sold Conditionally'),
('CB', 'Custom Banner'),
)
image = models.ImageField(upload_to=user_directory_path, blank=True)
mls_number = models.CharField(max_length=8, blank=True)
headline = models.CharField(max_length=30)
details = RichTextField()
banner = models.CharField(max_length=2, choices=BANNER_CHOICES)
author = models.ForeignKey(User, on_delete=models.CASCADE)
date_posted = models.DateTimeField(default=timezone.now)
date_modified = models.DateTimeField(default=timezone.now)
program_code = models.ManyToManyField(Program)
product = models.ForeignKey('Product', on_delete=models.SET_NULL, null=True)
production_cycle = models.ManyToManyField('ProductionCycle')
shell = models.ForeignKey('Shell', on_delete=models.SET_NULL, null=True)
card_delivery_instructions = models.CharField(max_length=1000, blank=True)
card_delivery_instructions_image = models.ImageField(upload_to=card_delivery_instructions_image_path, blank=True)
form:
class PropertyCreateKeepInTouchForm(forms.ModelForm):
class Meta:
model = PropertySubmission
fields = ['headline','details','banner','image','mls_number','program_code']
help_texts = {
'details': '110 characters maximum',
}
def clean(self):
cleaned_data = super().clean()
image = cleaned_data.get("image")
mls_number = cleaned_data.get("mls_number")
program_code = cleaned_data.get("program_code")
if mls_number == '' and image is None:
# Only do something if one field are valid so far.
self.add_error('image', 'Please provide an image or MLS number')
self.add_error('mls_number', 'Please provide an image or MLS number')
raise forms.ValidationError("Please provide a number")
if program_code is None:
self.add_error('program_code', 'Please select one or more programs')
raise forms.ValidationError("Please select one or more program")
def __init__(self, user, *args, **kwargs):
super(PropertyCreateKeepInTouchForm, self).__init__(*args, **kwargs)
#self.fields['details'].widget = forms.Textarea(attrs={'rows':4, 'cols':15, 'maxlength':110})
self.fields['details'].label = "Provide Feature Property Details:"
self.fields['program_code'].widget = forms.CheckboxSelectMultiple()
self.fields['program_code'].queryset = Program.objects.filter(client=user).filter(production_cycles__product_name__name='Keep In Touch').filter(production_cycles__submission_deadline__lt=timezone.now()+timedelta(days=30)).filter(production_cycles__submission_deadline__gt=timezone.now()).distinct()
self.fields['program_code'].label = "Select Programs"
self.fields['mls_number'].label = "number - to retrieve a photo of the property."
self.fields['image'].label = "Property Photo"
self.fields['banner'].label = "Select a banner"
self.fields['headline'].label = "Provide a headline"
view:
from django.forms import modelformset_factory
#login_required
def create_properties_keepintouch(request, num_forms):
property_formset = modelformset_factory(PropertySubmission, form=PropertyCreateKeepInTouchForm, extra=1)
formset = property_formset(queryset=PropertySubmission.objects.none())
return render(request, 'programs/property_submission_create_formset.html', {'formset': formset, 'num_forms':num_forms})
template:
<form class="form-horizontal" method="POST" action="">
{% csrf_token %}
{{ formset.management_form }}
{% for form in formset %}
<div class="row form-row spacer">
<div class="col-6">
<hr>
<div class="input-group">
{{ form.media }}
{{form.as_p}}
</div>
</div>
</div>
{% endfor %}
<div class="row spacer">
<div class="col-4 offset-2">
<button type="submit" class="btn btn-block btn-primary">Create</button>
</div>
</div>
</form>
I believe you'd want to use form_kwargs:
formset = property_formset(
queryset=PropertySubmission.objects.none(),
form_kwargs={'user': request.user}
)
You have overloaded PropertyCreateKeepInTouchForm __init__ method as you need to pass a user. However as far as I understand, modelform_factory and modelformset_factory functions don't allow passing arbitrary arguments.
So to the best of my knowledge, you will need to create your formset manually, not with a factory function.
The easiest way to accomplish this is to use functools.partial:
from functools import partial
def create_properties_keepintouch(request, num_forms):
form = partial(PropertyCreateKeepInTouchForm, request.user)
property_formset = modelformset_factory(PropertySubmission, form=form, extra=1)
...
Basically, this passes the user argument to the __init__ function before you actually call it, which allows Django to initialize the form class itself.

Django - ManyToManyField does not let me save, "This field is required" even though I select something

I have a formset like so:
class TransactionForm(ModelForm):
def __init__(self, *args, **kwargs):
try:
user = kwargs.pop("user")
except KeyError:
user = None
super(TransactionForm, self).__init__(*args, **kwargs)
self.fields["date"].widget.attrs["class"] = "datepicker"
if user is not None:
self.fields["categories"].queryset = Category.objects.get_all(user)
self.fields["account"].queryset = Account.objects.for_user(user)
class Meta:
model = Transaction
exclude = [""]
TransactionFormSet = modelformset_factory(Transaction, form=TransactionForm, exclude=("",))
View:
def transaction_create_view(request, account_id=None):
if request.method == "POST":
formset = TransactionFormSet(request.POST)
print(formset.errors)
if formset.is_valid():
for form in formset:
if form.is_valid and form.has_changed():
form.save()
if account_id is not None:
transactions = TransactionFormSet(queryset=Transaction.objects.for_account(account_id, request.user))
else:
transactions = TransactionFormSet(queryset=Transaction.objects.for_user(request.user))
transactions.form = curry(TransactionForm, user=request.user)
transactions.forms.insert(0, transactions.forms[-1])
del transactions.forms[-1]
context = {"transactions":transactions,}
return render(request, "transactions/transactions.html", context)
Model:
class Transaction(models.Model):
account = models.ForeignKey(Account)
date = models.DateField()
payee = models.CharField(max_length = 100)
categories = models.ManyToManyField(Category)
comment = models.CharField(max_length = 1000)
outflow = models.DecimalField(max_digits=10, decimal_places=3)
inflow = models.DecimalField(max_digits=10, decimal_places=3)
cleared = models.BooleanField()
class Category(models.Model):
title = models.CharField(max_length = 100)
subcategory = models.ForeignKey("self", blank=True, null=True)
user = models.ForeignKey(User)
budget = models.DecimalField(max_digits=10, decimal_places=2)
And template:
<form action="{% url 'transaction_create' %}" method="post">
{% csrf_token %}
{{ transactions.management_form }}
{% for form in transactions %}
{{ form.as_p }}
<input type="submit" value="Save transaction" />
<hr>
{% endfor %}
</form>
I input information in the empty form it produces and click on the save button, but the formset is not valid and I get this when I print the errors:
[{'categories': ['This field is required.']}, {}]
The categories in the template are represented by a <select>, where I select a category (the background is colored). Apparently it is not set though and thus I can't save, why is that? How can I fix it?
EDIT:
I have no idea why but now it works.
I reset the database re-created some data to see if that fixes things, and it did, I didn't change anything in the code.
If you override the save() in TransactionForm, you have to call save_m2m() to save the m2m objects.
https://docs.djangoproject.com/en/1.10/topics/forms/modelforms/

django form validation not showing

i have this model :
class Member(models.Model):
profile = models.OneToOneField(Profile, editable=False, null=True)
title = models.CharField(max_length=4, choices=TITLE_TYPES, null=True)
name = models.CharField(max_length=100, null=True, verbose_name='Name')
lastname = models.CharField(max_length=100, null=True, verbose_name='LastName')
gender = models.CharField(max_length=1, choices=GENDER_CHOICES, null=True, verbose_name='Gender')
dob = models.DateField('dob')
redressno = models.CharField(max_length=100, null=True, verbose_name='RedressNo')
form :
class MemberForm(ModelForm):
dob = forms.DateField(required=False, input_formats=('%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y'))
class Meta:
model = Member
exclude = ('profile',)
view :
MemberFormSet = formset_factory(MemberForm, formset=BaseFormSet)
print request.method
if request.method == 'POST': # If the form has been submitted...
signup_form = SignupForm(None, request.POST) # A form bound to the POST data
# Create a formset from the submitted data
member_formset = MemberFormSet(request.POST, request.FILES)
# import pdb; pdb.set_trace()
if signup_form.is_valid() and member_formset.is_valid():
print 'in valid'
signup = signup_form.save(request.POST)
for form in member_formset.forms:
member = form.save(commit=False)
member.profile = signup
# import pdb; pdb.set_trace()
member.save()
#log-in to user
user = authenticate(username = request.POST['username'], password = request.POST['password'] )
auth_login(request, user)
return redirect("/main") # Redirect to a 'success' page
else:
signup_form = SignupForm()
data = {
'form-TOTAL_FORMS': u'1',
'form-INITIAL_FORMS': u'0',
'form-MAX_NUM_FORMS': u''
}
member_formset = MemberFormSet(data)
# member_formset.total_form_count=0
# For CSRF protection
# See http://docs.djangoproject.com/en/dev/ref/contrib/csrf/
c = {
'form': signup_form,
'member_formset': member_formset,
}
c.update(csrf(request))
return render_to_response('registration.html', c, RequestContext(request))
Template :
<form action="/registration/" method="POST">
{% csrf_token %}
<div class="section">
{{ form.as_p }}
</div>
<h2>Members</h2>
{{ member_formset.management_form }}
{% for form in member_formset.forms %}
<div class="item">
{{ form.as_table }}
<p style=""><a class="delete" href="#">Delete</a></p>
</div>
{% endfor %}
<p><a id="add" href="#">Add another item</a></p>
<input type="submit" value=" Submit " />
and validation on memberform is not working .Please suggest why ?
You don't really explain your problem, but I'd guess you're not proceeding to validation of the member formset if the signup form isn't valid, because of this line:
if signup_form.is_valid() and member_formset.is_valid():
In Python, as in many other languages, and is shortcut-evaluated: that is, if the first element is False, the second is never evaluated. So is_valid is never run on the formset, so no errors are shown.
Instead, do this:
signup_valid = signup_form.is_valid()
formset_valid = member_formset.is_valid()
if signup_valid and formset_valid:
which ensures that both validations are performed separately.
Firstly, I don't see an else branch for invalid forms. Secondly, isn't the first parameter , None, erroneous for the form initialization in this line: signup_form = SignupForm(None, request.POST).