Pure Django Form Tying User To Submitted Form And View - django

I am unsure how to tie a logged in user to a submitted form using regular Django forms. I see alot of examples using ModelForms but none (that I can tell) without using the ModelForms. In my forms.py im having a hard time figuring out how to add the author field. I cannot just add author = forms.ForeignKey or something like that. Then somehow in my view i need to call the author field to be saved into the database (my below example is my best guess and probably not right with the "tenant_form.author = request.user").
I have a model that looks like this and has a user Foreignkey setup:
class AppyModel(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True)
date_time_form_filled = models.DateTimeField(auto_now_add=True)
first_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
I have a forms.py:
class TenantForm(forms.Form):
first_name = forms.CharField(required=False, label='First Name')
last_name = forms.CharField(required=False, label='Last Name')
I have a views.py
#login_required
def tenant_create_form_view(request):
tenant_form = TenantForm()
if request.method == 'POST':
tenant_form.author = request.user
tenant_form = TenantForm(request.POST)
if tenant_form.is_valid():
print(tenant_form.cleaned_data)
AppyModel.objects.create(**tenant_form.cleaned_data)
else:
print(tenant_form.errors)
context = {
'form': tenant_form
}
return render(request, 'fill_appy.html', context)

You should add author when the form is valid,
tenant_form = TenantForm()
if request.method == 'POST':
tenant_form = TenantForm(request.POST)
if tenant_form.is_valid():
obj = tenant_form.save(commit=False)
obj.author = request.user #add author here
obj.save()
# .. rest of code

Related

With Django, how do I reference an existing model in a form save method instead of creating a new instance?

I'm trying to use a ModelChoiceField to display options populated from model, and when a user selects a choice, store that method in a different model.
I'm using a standard form instead of a ModelForm, because I wasn't able to get the form to display how I wanted to when using a Modelform.
My issue is that in my form save method, a new instance is created, which is not what I want.
Here are the relevant models:
class Client(models.Model):
client_email = models.EmailField(max_length = 254)
first_name = models.CharField(max_length=200)
last_name = models.CharField(max_length=200)
phone = PhoneField(blank=True)
assigned_manager = models.ForeignKey(Manager, on_delete=models.CASCADE, blank=True, null=True)
created_date = models.DateTimeField(default=timezone.now)
#property
def full_name(self):
return '{0} {1}'.format(self.first_name, self.last_name)
class Manager(models.Model):
first_name = models.CharField(max_length=200)
last_name = models.CharField(max_length=200)
manager_email = models.EmailField(max_length = 254)
username = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, blank=True, null=True)
#property
def full_name(self):
return '{0} {1}'.format(self.first_name, self.last_name)
My view:
def manageclient(request, urlid):
client = Client.objects.get(id=urlid)
form = AssignManagerForm()
if request.method == "POST":
form = AssignManagerForm(request.POST)
if form.is_valid():
form.save()
return render(request, 'mysite/manageclient.html', {})
else:
form = AssignManagerForm()
context = {
'client': client,
'urlid': urlid,
'form': form,
}
return render(request, 'mysite/manageclient.html', context)
And my forms.py
class AssignManagerForm(forms.Form):
full_name = forms.ModelChoiceField(queryset=Manager.objects.all())
def save(self):
data = self.cleaned_data
client = Client(assigned_manager=data['full_name'])
client.save()
What I need to do is pass the urlid in my view to my save method in my forms.py, but I am unsure how to do that. Even if i could do that, I'm not sure how to modify form save to use urlid to refer to a specific record and set only the assigned_manager record.
Additionally, while I want the meta field to be used to display the form, I know it isn't what should be being passed to the assigned_manager field. How would I pass a Manager of instance to establish the foreign key relationship?
edit: edited to correct queryset in forms.py as per comments
Here is a solution using a ModelForm, by using a ModelForm you no longer have to manually set attributes on save or provide initial values when updating an existing instance.
The field assigned_manager will still be named assigned_manager but it's label can be overridden to be whatever you want it to be by passing labels in the ModelForm.Meta
class AssignManagerForm(forms.ModelForm):
class Meta:
model = Client
fields = ['assigned_manager']
labels = {'assigned_manager': 'Full name'}
def manageclient(request, urlid):
client = Client.objects.get(id=urlid)
if request.method == "POST":
form = AssignManagerForm(request.POST, instance=client)
if form.is_valid():
client = form.save()
# The general convention is to redirect after a successful POST
else:
form = AssignManagerForm(instance=client)
context = {
'client': client,
'urlid': urlid,
'form': form,
}
return render(request, 'mysite/manageclient.html', context)
Instead of saving it in form, you can directly do this operation in view. For example:
def manageclient(request, urlid):
client = Client.objects.get(id=urlid)
if request.method == "POST":
form = AssignManagerForm(request.POST)
if form.is_valid():
client.assigned_manager = form.cleaned_data['full_name']
client.save()
return render(request, 'mysite/manageclient.html', {})

How to retrieve user selections from a previous form

I have several forms that take people through steps and below are the first two and the simplest ones and makes it easy to explain what i am having problem with.
The following two views are login required and contain one form on each. First view is the new_operator where the user fills out a single text input field. Second view is the new_asset where the user fills one text input field as the asset name and selects an operator from the a select/dropdown field. The question is how can i get the form to remember the operator name the user created in the previous form and make it as the default option? To be clear, i still want the user to select any other operator if they choose to do so but i want the option they just created to be the default. Thanks a lot in advance for the help.
First, here are the models:
class OperatorCompany(models.Model):
name = models.CharField(max_length=50, unique=True)
created_at = models.DateTimeField(default=timezone.now)
created_by = models.ForeignKey(User, related_name='operator_added_by', null=True, on_delete=models.SET_NULL)
class Meta:
verbose_name = "Operator Company"
verbose_name_plural = "Operator Companies"
def __str__(self):
return self.name
class AssetName(models.Model):
name = models.CharField(max_length=50, unique=True)
operator = models.ForeignKey(OperatorCompany, related_name='asset', on_delete=models.CASCADE)
created_at = models.DateTimeField(default=timezone.now)
created_by = models.ForeignKey(User, related_name='asset_added_by', null=True,
on_delete=models.SET_NULL)
class Meta:
verbose_name = "Asset"
verbose_name_plural = "Assets"
def __str__(self):
return self.name
views.py
def new_operator(request):
if request.method == 'POST':
form = NewOperatorForm(request.POST)
if form.is_valid():
newoperator = form.save(commit=False)
newoperator.created_by = request.user
newoperator.created_at = timezone.now()
newoperator.save()
return redirect('wellsurfer:new_asset')
else:
form = NewOperatorForm()
return render(request, 'wellsurfer/create_new_operator.html', {'create_operator': form})
def new_asset(request):
if request.method == 'POST':
form = NewAssetForm(request.POST)
if form.is_valid():
newasset = form.save(commit=False)
newasset.created_by = request.user
newasset.created_at = timezone.now()
newasset.save()
return redirect('wellsurfer:new_pad')
else:
form = NewAssetForm()
return render(request, 'wellsurfer/create_new_asset.html', {'create_asset': form})
and following are the forms.py without the init, clean functions and the widgets
class NewOperatorForm(forms.ModelForm):
class Meta:
model = OperatorCompany
fields = ('name',)
class NewAssetForm(forms.ModelForm):
class Meta:
model = AssetName
fields = ('name', 'operator')
To share data between multiple pages, you can use session variables. These are stored on the server and associated to clients according to the session cookie they communicate to the server at every request.
Typically, in the first view, you would add after save():
request.session['latest_created_operator_id'] = newoperator.id
to save in the session the operator id.
And in the second view, after the else,
operator_id = request.session.get('latest_created_operator_id', None)
operator = Operator.objects.filter(id=operator_id).first() # returns None if not found
form = NewAssetForm(initial={'operator': operator})
retrieves the operator and populates the form.
(That's untested code; you may need to edit a bit.)
At a glance, maybe something like this would work.
What you can do is add another URL in urls.py for new_asset which accepts a OperatorCompany id. I don't have your url config but it could be something like:
urls.py
path('wellsurfer/new_asset/<int:operator_id>', new_asset, name='wellsurfer:new_asset_operator')
view.py
def new_operator(request):
if request.method == 'POST':
form = NewOperatorForm(request.POST)
if form.is_valid():
newoperator = form.save(commit=False)
newoperator.created_by = request.user
newoperator.created_at = timezone.now()
newoperator.save()
return redirect('wellsurfer:new_asset', operator_id=newoperator.id)
else:
form = NewOperatorForm()
return render(request, 'wellsurfer/create_new_operator.html', {'create_operator': form})
def new_asset(request, operator_id=None):
if request.method == 'POST':
form = NewAssetForm(request.POST)
if form.is_valid():
newasset = form.save(commit=False)
newasset.created_by = request.user
newasset.created_at = timezone.now()
newasset.save()
return redirect('wellsurfer:new_pad')
else:
form = NewAssetForm()
if operator_id is not None:
operator_company = OperatorCompany.objects.get(pk=operator_id)
form.fields['operator'].initial = operator_company
return render(request, 'wellsurfer/create_new_asset.html', {'create_asset': form})

Django editing user's posts

I am working in a blog application using django. A user can add posts and edit their own posts. However, I am having trouble with retrieving user's posts. Here is a similar question, but it does not work with my application: Django edit form based on add form? Any help is truly appreciate
Here is my code:
#login_required
def edit(request, id):
if id:
post = get_object_or_404(BlogPost, id=id)
if post.author != request.user:
return render(request, "403.html")
else:
post = BlogPost(author=request.user)
if request.method == "POST":
form = AddPost(request.POST, instance=post)
if form.is_valid():
post = form.save(commit=False)
post.save()
messages.add_message(request, messages.SUCCESS,
'You have succesfully updated your post')
return redirect('homepage')
else:
form = AddPost(instance=post)
return render(request, 'blog/update.html', {'form': form})
Here is my model:
class BlogPost(models.Model):
title = models.CharField(
max_length=100, null=False, blank=False, unique=True)
content = models.TextField()
date_created = models.DateTimeField(auto_now_add=True)
date_updated = models.DateTimeField(auto_now=True)
author = models.CharField(max_length=50).......
Your author field is a CharField. I'm not sure what you're putting into that field, or how you're setting it, but it will never equal request.user, because that is an instance of your User model.
Normally the author field would be a ForeignKey to User, so that the comparison would be true for those posts authored by the current user. If you are putting the username into the author field, you could compare with that: if post.author != request.user.username - but I would really not recommend doing that, as you are breaking normalization for no good reason.
(Also note my other comment: your first else clause looks like it should be one indent to the left: ie under if id, not if post.author....)

Show modelform with the data the user filled in

Im trying to make a simple voting app.
The user should be presented with a blank form at first, but when the user has filled in the form, the user should be presented with the same form, but with the data they filled in.
How can I present the data they put in, on the same form?
My model:
class Vote(models.Model):
user = models.ForeignKey(User)
vote_1 = models.ForeignKey(Song, null=True, blank=True, related_name="voted_1")
vote_2 = models.ForeignKey(Song, null=True, blank=True, related_name="voted_2")
vote_3 = models.ForeignKey(Song, null=True, blank=True, related_name="voted_3")
creation_date = models.DateTimeField(auto_now_add=True)
edited = models.DateTimeField(auto_now=True)
My view:
def show_voteform(request):
if request.method == 'POST':
form = VoteForm(request.POST)
if form.is_valid():
form.save()
messages.success(request, "Vote saved", extra_tags='alert-success')
#Return the user to same page
return HttpResponseRedirect('/vote/')
else:
form = VoteForm(initial={'user':request.user, 'vote_1':???, 'vote_2':???, 'vote_3':???,})
return render(request, 'vote/form.html', {
'form': form,
})
Is this something I could provide in initial, or do I have to do this another way?
Edit:
Changed it to this:
else:
try:
vote = Vote.objects.filter(user=request.user).latest('creation_date')
form = VoteForm(instance=vote)
except Vote.DoesNotExist:
form = VoteForm(initial={'user':request.user})
If your VoteForm is a ModelForm, then you can show the form with data from a model instance filled in using:
# get the most recent Vote by this user
vote = Vote.objects.filter(user=request.user).latest('creation_date')
# fill in the form with data from the instance
form = VoteForm(instance=vote)

saving django ManyToMany not valid

I have a form from my model that needs to be validated and saved making use of ManyToMany Fields.
Everytime I try and save it, I get thrown back to the page, just saying this field is required
My models.py
class HuntingReport(models.Model):
user = models.ForeignKey(User, related_name='User')
outfitter = models.ForeignKey(User, related_name='Outfitter', null=True, blank=True)
date_travel_started = models.DateField(blank=True, null=True)
date_travel_ended = models.DateField(null=True, blank=True)
title = models.CharField(max_length=50)
report = models.TextField()
wish_list = models.ManyToManyField(Specie)
bag_list = models.ManyToManyField(Trophies)
def __unicode__(self):
return self.title
My forms.py looks as follows
class HuntingReportForm(ModelForm):
date_travel_started = forms.DateField(widget=extras.SelectDateWidget(years=range(1970,2010)))
date_travel_ended = forms.DateField(widget=extras.SelectDateWidget(years=range(1970,2010)))
wish_list = forms.ModelMultipleChoiceField(queryset=Specie.objects.all(), widget=FilteredSelectMultiple("verbose name", is_stacked=False))
bag_list = forms.ModelMultipleChoiceField(queryset=Trophies.objects.all(), widget=FilteredSelectMultiple("verbose name", is_stacked=False))
class Meta:
model = HuntingReport
exclude = ['user']
def __init__(self, user, *args, **kwargs):
super(HuntingReportForm, self).__init__(*args, **kwargs)
users = User.objects.filter(userprofile__outfitter=True)
self.fields['outfitter'].choices = [('', '')] + [(user.pk, user.get_full_name()) for user in users]
my views.py
def create(request, template_name='reports/new.html'):
if request.method == 'POST':
form = HuntingReportForm(request.POST, request.FILES)
if form.is_valid():
newform = form.save(commit=False)
newform.user = request.user
newform.save_m2m()
return HttpResponseRedirect('/hunting-reports/')
else:
form = HuntingReportForm(request.user)
context = { 'form':form, }
return render_to_response(template_name, context,
context_instance=RequestContext(request))
Did you try passing blank=True for model field's constructor, or required=False for the ModelMultipleChoiceField's constructor?
I know that blank=True solves the problem for the form in the admin panel, but I don't know how it gets mapped to the ModelForm's fields. I'm assuming that it gets mapped to required property.