0

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)

2 Answers 2

2

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'))
# ...
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you this is exactly what I needed! Thanks
0

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.

1 Comment

You were right in the sense that the database thought the User does not exist. However, the user does exist as the @login_required ensures this. My real issue was that I wasn't sure how to pass the user object to the field on submit, apologies for not making this clearer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.