My django website would like to allow logged in users to post a recipe. The recipe model is linked to the user model and as such requires a valid user instance. I would like the Recipe form to automatically assign the postedby field to the instance of the logged in user.
So far I have attempted to pass in a dictionary, storing the user's name, to the form instance. as shown in the view
However, the constructor method is not receiving the data and is rendering the form but with a failed attempt to submit.
I cannot store the data without postedby field having a valid instance as the model throws the following error:
Exception Value:UserProfile matching query does not exist.
I have also tried to do the following in views.py;
#login_required
def add_recipe(request):
form = RecipeForm()
form.fields['postedby'] = UserProfile.objects.get(user=User.objects.get(username=request.user.__str__()))
context_dict = {'form':form}
if request.method == 'POST':
...
However this overwrites the postedby form view to be rendered and raises and error.
views.py
#login_required
def add_recipe(request):
form = RecipeForm({'user':request.user})
context_dict = {}
#print(form.fields['postedby'].queryset)
if request.method == 'POST':
form = RecipeForm(request.POST)
if form.is_valid():
form.save(commit=True)
return redirect(reverse('spatula:index'))
else:
print(form.errors)
context_dict['form'] = form
return render(request, 'spatula/add_recipe.html', context=context_dict)
The RecipeForm is as follows:
class RecipeForm(forms.ModelForm):
def __init__(self,*args,**kwargs):
print(kwargs)
super(RecipeForm, self).__init__(*args, **kwargs)
#input fields for recipe form
method = forms.CharField(max_length=512, widget=forms.Textarea(attrs={'placeholder':'Method'}))
name = forms.CharField(max_length=128, widget=forms.TextInput(attrs={'placeholder':'Recipe Name'}))
ingredients = forms.CharField(max_length=512, widget=forms.Textarea(attrs={'placeholder':'Ingredients'}))
category = NameChoiceField(widget=forms.Select(), queryset =Category.objects.all(), initial = 0)
toolsreq = forms.CharField(max_length=512, widget=forms.TextInput(attrs={'placeholder':'Tools Required'}))
difficulty = forms.IntegerField(widget=forms.NumberInput(attrs={'type':'range', 'step':'1', 'min':'1','max':'3'}), help_text = 'Difficulty: ')
cost = forms.IntegerField(widget=forms.NumberInput(attrs={'type':'range', 'step':'1', 'min':'1','max':'3'}), help_text = 'Cost: ')
diettype = forms.IntegerField(widget=forms.RadioSelect(choices=DIET_CHOICES))
# not required as its not stored in DB
#description = forms.CharField(widget=forms.Textarea(attrs={'placeholder':'Description'}))
#hidden fields
rating = forms.FloatField(widget=forms.HiddenInput(), initial=0, required=False)
slug = forms.SlugField(widget=forms.HiddenInput(),required=False)
postedby = forms.SlugField(widget=forms.HiddenInput(),required=False)
#Order in which inputs get rendered
field_order = ['name', 'category', 'toolsreq', 'difficulty', 'cost', 'diettype', 'ingredients', 'method']
class Meta:
model = Recipe
exclude = ('id',)
finally here is the recipe model:
class Recipe(models.Model):
DIET_CHOICES = (
(1,"Meat"),
(2,"Vegetarian"),
(3,"Vegan"),
)
DIFFICULTY_CHOICES = (
(1,1),
(2,2),
(3,3),
)
COST_CHOICES = (
(1,1),
(2,2),
(3,3),
)
name = models.CharField(max_length=NAME_MAX_LENGTH)
ingredients = models.TextField(max_length=MAX_TEXT_LENGTH)
toolsreq = models.TextField(max_length=MAX_TEXT_LENGTH)
method = models.TextField()
# make sure the form views for difficulty,
# cost and diettype datatypes restrict the
# users selection to the CHOICES above
difficulty = models.PositiveSmallIntegerField(choices=DIFFICULTY_CHOICES)
cost = models.PositiveSmallIntegerField(choices=COST_CHOICES)
diettype = models.PositiveSmallIntegerField(choices=DIET_CHOICES)
postedby = models.ForeignKey(UserProfile, on_delete=models.CASCADE, default=0)
# - Following fields are hidden when creating a new recipe
# ratings are out of 5, to 1 decimal place.
# - Need a script to update rating everytime
# a new rating for the recipe is posted.
rating = models.DecimalField(decimal_places=1,max_digits=3, default=0)
category = models.ForeignKey(Category,to_field="name", on_delete=models.CASCADE)
# recipes rating is calculated when the recipe is requested, no value to be stored
def __str__(self):
return self.name
# used for recipe mappings
def save(self,*args, **kwargs):
self.slug = slugify(str(self.name)+str(self.postedby))
super(Recipe,self).save(*args, **kwargs)
Since you don't want your user go edit this field, remove it entirely from the form:
exclude = ['id', 'postedby']
Then in your view, set the value on the instance before saving:
# ...
if request.method == 'POST':
form = RecipeForm(request.POST)
if form.is_valid():
recipe = form.save(commit=False)
recipe.postedby = UserProfile.objects.get(user=request.user)
recipe.save()
return redirect(reverse('spatula:index'))
# ...
The error says: Exception Value:UserProfile matching query does not exist. It means there is no UserProfile object for that.
You probably want:
try:
profile = UserProfile.objects.get(user=request.user)
except UserProfile.DoesNotExist
# your logic
Or if you want to automatically create UserProfile object for requested user. You can use get_or_create:
p, created = UserProfile.objects.get_or_create(user=request.user)
Explanation: Any keyword arguments passed to get_or_create() — except an optional one called defaults — will be used in a get() call. If an object is found, get_or_create() returns a tuple of that object and False.
Related
I am trying to associate the user with the post. I have two models students is for user and sublists is for user posts with a foreign key(author). I am using MySQL database and using forms to store data into them. when my form.author execute in my HTML file it gives me a list of ids for all users in the databse but I am already logged in and i want to post as the logged in user without choosing. If remove it says my form is not valid which make sense since im not inputing for form.author.Since I'm using MySQL, I'm not using the built-in User authentication method, but instead comparing both email and password with the login form input. Spend too much time on this but hard to get around with this one. Any help would be appreciated
my views.py look like this
def addnew(request):
if request.method == 'POST':
form = Sublist(request.POST)
if form.is_valid():
try:
form.save()
messages.success(request, ' Subscirption Saved')
name = sublist.objects.get(name=name)
return render (request, 'subscrap/main.html', {'sublist': name})
except:
pass
else:
messages.success(request, 'Error')
pass
else:
form = Sublist()
return render(request, 'subscrap/addnew.html', {'form': form})
#login_required(login_url='login')
#cache_control(no_cache=True, must_revalidate=True, no_store=True)
def main(request):
return render(request, 'subscrap/main.html')
def mod(request):
student = students.objects.all()
return render(request, 'subscrap/mod.html' , {'students': student})
My Models.py
class students(models.Model):
fname = models.CharField(max_length=50)
lname = models.CharField(max_length=50)
password = models.CharField(max_length = 50 , null = True)
passwordrepeat = models.CharField(max_length = 50, null = True)
email = models.EmailField(max_length=150)
class Meta:
db_table = "students"
class sublist(models.Model):
author = models.ForeignKey(students, related_name='sublist' ,on_delete=models.CASCADE)
name = models.CharField(max_length=150)
cost = models.IntegerField(default = 0)
renewalcycle = models.IntegerField(default = 0)
class Meta:
db_table = "sublist"
Since I'm using forms here's my forms.py
lass StudentForm(forms.ModelForm):
class Meta:
model = students
fields = "__all__"
class Studentlogin(forms.Form):
email = forms.EmailField(max_length=150)
password = forms.CharField(max_length = 50, widget=forms.PasswordInput)
class Sublist(forms.ModelForm):
class Meta:
model = sublist
fields = "__all__"
Exclude the Author from the Sublist form:
class Sublist(forms.ModelForm):
class Meta:
model = sublist
exclude = ['author']
In the addnew method, you associate the .instance.author with the request.user:
#login_required(login_url='login')
def addnew(request):
if request.method == 'POST':
form = Sublist(request.POST)
if form.is_valid():
form.instance.author = request.user
form.save()
messages.success(request, ' Subscirption Saved')
return redirect('some_view')
else:
messages.error(request, 'Error')
else:
form = Sublist()
return render(request, 'subscrap/addnew.html', {'form': form})
Note: Models in Django are written in PascalCase, not snake_case,
so you might want to rename the model from sublist to Sublist.
Note: Usually a Form or a ModelForm ends with a …Form suffix,
to avoid collisions with the name of the model, and to make it clear that we are
working with a form. Therefore it might be better to use SublistForm instead of
Sublist.
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the students directly. For more information you can see the referencing the User model section of the documentation.
I have two Models with a ManyToMany relationship. In my view I want to process the user input of the MultipleChoicesField and assign the selected choices to the Poller object.
It raises the following error:
Direct assignment to the forward side of a many-to-many set is
prohibited. Use poller_categories.set() instead.
Models.py
class Categories(models.Model):
poller_category = models.CharField(max_length=30)
poller_category_id = models.IntegerField(default=0)
def __str__(self):
return str(self.poller_category)
class Pollers(models.Model):
[..]
# Poller Category
poller_categories = models.ManyToManyField(Categories)
def __str__(self):
return str(self.poller_id)
Forms.py
class PollersForm(forms.Form):
[..]
# Poller Tags
poller_categories = forms.ModelMultipleChoiceField(queryset=Categories.objects.all())
views.py
def raise_poller(request):
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = PollersForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
poller_nature = form.cleaned_data['poller_nature']
poller_text = form.cleaned_data['poller_text']
poller_choice_one = form.cleaned_data['poller_choice_one']
poller_choice_two = form.cleaned_data['poller_choice_two']
poller_categories = form.cleaned_data['poller_categories']
# Get the user
created_by = request.user
# Save the poller to the database
p = Pollers(poller_nature = poller_nature,
poller_text = poller_text,
poller_choice_one = poller_choice_one,
poller_choice_two = poller_choice_two,
poller_categories = poller_categories, # here seems to be my issue?
created_by = created_by)
p.save()
# redirect to a new URL:
return HttpResponseRedirect('/')
You can not directly assign a value to the poller_categies. You should first save the Pollers object to the database, and then use p.poller_categories.set(…) to populate the many-to-many: relation
p = Pollers(
poller_nature = poller_nature,
poller_text = poller_text,
poller_choice_one = poller_choice_one,
poller_choice_two = poller_choice_two,
# no poller_categories,
created_by = created_by
)
p.save()
p.poller_categories.set(poller_categories)
The basic reason for this is likely that you need the primary key of both objects before you can link two items together, so at that moment there is not (yet) a record in the database for p.
I am trying to pass logged in user to form that i would like to save.
forms.py
class SpotForm(ModelForm):
def __init__(self, *args, **kwargs):
super(SpotForm, self).__init__(*args, **kwargs)
self.fields['gross_weight'].widget = forms.NumberInput(attrs={'min':0})
self.fields['volume'].widget = forms.NumberInput(attrs={'min': 0})
class Meta:
model = Spot
fields = [
'gross_weight','volume','origin_country','origin_port',
'dest_country','dest_port','ship_week','requestor'
]
models.py
class Stakeholder(models.Model):
user = models.OneToOneField(User,null=True,blank=True,on_delete=models.CASCADE)
company_name = models.CharField(max_length=15)
mail = models.CharField(max_length=40)
def __str__(self):
return self.mail
class Spot(models.Model):
STATUSES = (
('Open','Open'),
('Closed','Closed')
)
gross_weight = models.FloatField(null=False,default=0,validators=[MinValueValidator(0)])
volume = models.FloatField(null=False,default=0,validators=[MinValueValidator(0)])
origin_country = models.CharField(
validators=[RegexValidator(regex='[A-Z]{2}', message='Country code is two letters')], max_length=2,null=True)
origin_port = models.CharField(
validators=[RegexValidator(regex='[A-Z]{3}', message='Port code is three letters')], max_length=3,null=True)
dest_country = models.CharField(
validators=[RegexValidator(regex='[A-Z]{2}', message='Country code is two letters')], max_length=2,null=True)
dest_port = models.CharField(
validators=[RegexValidator(regex='[A-Z]{3}', message='Port code is three letters')], max_length=3,null=True)
time_registered = models.DateField(default=timezone.now)
spot_status = models.CharField(max_length=6,default='Open', choices=STATUSES)
ship_week = models.CharField(max_length=2,null=True)
requestor = models.ForeignKey(Stakeholder,null = True,on_delete=models.CASCADE)
def __str__(self):
return self.origin_country + self.origin_port + '-' + self.dest_country +self.dest_port + '-' + self.ship_week
views.py
def register_spot(request):
my_user = Stakeholder.objects.get(user=request.user)
form = SpotForm()
if request.method =='POST':
print("print",request.POST)
form = SpotForm(request.POST)
if form.is_valid():
form.save()
return redirect('/')
else:
print(form.errors)
context = {'form': form}
return render(request, 'spotrequesting/register_spot.html', context)
When i submit the form i am getting an error in command prompt stating "This field is required" for "requestor". After that - dropdown list for this field come up on screen and i can select out of two registered users i have. But even selecting something from this list and again submitting the form is giving me the same error.
Checking "my_user" variable - it is showing me that i am logged in.
Is there a way to pass to "requestor" field currently logged in user?
I was able to get the form saved only by deleting "requestor" from "fields" in SpotForm (which gave me "None" in the end for this field in database) but that's not the desired outcome.
Any suggestion would be highly appreciated.
You are not really passing the stakeholder instance to the requestor field in the form are you? So you will have to do:
form = SpotForm(requestor = my_user)
I have created a form using python and django from 2 seperate modelForms in the one html template. Models:
class Action(models.Model):
name = models.CharField("Action name", max_length=50)
keywords = models.CharField("Keywords", max_length=50)
object = models.CharField("Object", max_length=50, blank=True, null=True)
uploadDate = models.DateField("Date", default=get_current_date)
UploadedBy = models.CharField("UploadedBy", max_length=50, default="")
class Image(models.Model):
image = models.FileField(upload_to=get_upload_file_name, default="")
action = models.ForeignKey(Action)
def get_upload_file_name(instance, filename):
return "uploaded_files/%s_%s" % (str(datetime.now().day).replace('.','_'), filename)
forms:
class ActionForm(ModelForm):
#bind form to Action model
class Meta:
model = Action
fields = ['name','keywords', 'object', 'UploadedBy', 'uploadDate']
class ImageForm(ModelForm):
class Meta:
model= Image
fields =['image']
The code which creates the form in views:
def actioncreate(request):
if request.method == "GET":
#create the object - Actionform
form = ActionForm;
form2 = ImageForm;
#pass into it
return render(request,'app/createForm.html', { 'form':form, 'form2':form2})
elif request.method == "POST":
# take all of the user data entered to create a new action instance in the table
form = ActionForm(request.POST, request.FILES)
form2 = ImageForm(request.POST, request.FILES)
if form.is_valid() and form2.is_valid():
act = form.save(commit=False)
img = form2.save(commit=False)
#set the action_id Foreignkey
act.id = img.action_id
act.save()
img.save()
return HttpResponseRedirect('/actions')
else:
form = ActionForm()
form2 = ImageForm;
return render(request,'app/createForm.html', { 'form':form, 'form2':form2 })
The form is created fine but when it is submitted, it trys to save image.id, image.image (filename) and returns null for image.action_id
I am getting the error:
null value in column "action_id" violates not-null constraint
DETAIL: Failing row contains (2, uploaded_files/29_personrunning_Hq8IAQi.jpg, null).
I obviously need to populate the third column with the action.id which django creates itself on submitting the first part 'form'. Is there a way I can get the action.id value and populate the action_id field in the image table in the one form submission?
image.action_id is declared initially as a foreignKey related to action in models.
The first problem is related to act = form.save(commit=False) because it will return an object that hasn’t yet been saved to the database, then act doesn't have an ID. You need to save (and commit) act first.
Also there is another error in following line:
act.id = img.action_id # It should be: img.action_id = act.id
You may want to assign act to img.action. Please note that you are doing it in the wrong way (you are assigning in img.action to act). The best way to do it is:
img.action = act # This is equivalent to img.action_id = act.id
Try swapping these lines:
act.save()
img.action = act
I have a form in my application which has a hidden form field, the value of which I want to set in my corresponding view after submitting the form.
forms.py
class EvangelizedForm(forms.ModelForm):
first_name = forms.CharField(help_text="First Name")
last_name = forms.CharField(help_text="Last Name")
email = forms.CharField(help_text="Email ID")
mobile_no = forms.CharField(help_text="Mobile number")
twitter_url = forms.CharField(help_text="Twitter URL")
twitter_followers = forms.CharField(widget = forms.HiddenInput()) #Hidden form field
class Meta:
model = Evangelized
fields = ('first_name','last_name', 'twitter_url', 'email', 'mobile_no')
models.py
class Evangelized(models.Model):
first_name = models.CharField(max_length=128)
last_name = models.CharField(max_length=128)
email = models.EmailField()
mobile_no = models.CharField(unique=True, max_length = 10, validators=[RegexValidator(regex='^\w{10}$', message='Mobile number should be strictly of 10 digits.')])
twitter_url = models.CharField(unique=True, max_length=128)
twitter_followers = models.CharField(max_length = 128)
views.py
def fillform(request):
follower_count = '250'
if request.method == 'POST':
form = EvangelizedForm(request.POST)
if form.is_valid():
form.fields['twitter_followers'] = follower_count
form.save(commit=True)
return index(request)
else:
form.errors
else:
#form = EvangelizedForm()
if request.user.is_authenticated():
form = EvangelizedForm(initial={'first_name': request.user.first_name,
'twitter_url': 'https://twitter.com/' + request.user.username,
'last_name': request.user.last_name})
else:
form = EvangelizedForm()
context = RequestContext(request,
{'request': request,
'user': request.user, 'form':form})
#return render(request, 'rango/fillform.html', {'form': form, 'context_instance':context})
return render_to_response('rango/fillform.html',
context_instance=context)
Basically, I'm trying to set the value of twitter_followers (which is a hidden form field in forms.py) in my index view, by:
follower_count = '250'
..
..
form.fields['twitter_followers'] = follower_count
By doing this, I'm expecting the value of 'twitter_followers' in the database after submitting the form to be '250'. However, this approach doesn't seem to be working.
What's the right way to set values to certain attributes in the database manually using views?
You need to set it on the model instance, which is the result of form.save. That's the main reason for the commit argument in the first place.
if form.is_valid()
obj = form.save(commit=True)
obj.twitter_follower = follower_count
obj.save()
You can override the save method of the form, with something like this:
def save(self, *args, **kwargs)
twitter_followers = kwargs.pop('twitter_followers', 0)
self.instance.twitter_followers = twitter_followers
super(Evangelized, self).save(args, kwargs)
And then in the view just have to call in this way:
form.save(twitter_followers=250)