Reputation: 17
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)
Upvotes: 0
Views: 1542
Reputation: 842
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.
Upvotes: 0
Reputation: 20692
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'))
# ...
Upvotes: 2