How to set ForeignKey in the model depending on a slug? - django

I have a registration form for an event. Since this registration form displays as a modal when clicking the 'Register' button on the event page, I know what event it is that the user want to register to. But Django doesn't, since I don't know how to implement this in code.
I have two models: Participant and Event. Each instance of Participant refers to an Event instance by means of ForeignKey. How do I set that ForeignKey depending on the slug of the event page?
This is my code example:
models.py:
from django.db import models
class Event(models.Model):
name = models.CharField(max_length=50)
description = models.CharField(max_length=500)
#<...>
slug = models.SlugField()
class Participant(models.Model):
name = models.CharField(max_length=200)
email = models.EmailField()
event = models.ForeignKey(Event, on_delete=models.CASCADE)
forms.py:
from django.forms import ModelForm
from .models import Participant
class ParticipantForm(ModelForm):
class Meta:
model = Participant
fields = ['name', 'email']
views.py:
from django.template.loader import render_to_string
from django.views import generic
from .models import *
from .forms import *
class RegistrationView(generic.FormView):
template_name = 'me/registration.html'
form_class = ParticipantForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['event'] = Event.objects.get(slug=self.args[0])
return context
def form_valid(self, form):
form.save()
return HttpResponse(render_to_string('me/registration-complete.html', {'event': Event.objects.get(slug=self.args[0])}))

You'd need to set it in form_valid. In this circumstance get_context_data wouldn't have been called, so you need to get the event again; you might want to extract that into a separate method.
def form_valid(self, form):
form.instance.event = Event.objects.get(reference_name=self.args[0])
form.save()
return ...

Related

In Django, how to add username to a Model automatically, when the Form is submitted by a logged in user

In my Django app, I have defined a Many-to-one relationship using ForeignKey. Now what I want is that when a logged-in user submits the ListForm then his username should automatically add to the owner field of the ListModel. Currently when a user submits the form, None is being shown in the owner field, how can I add the username to the database along with the form?
my models.py:
from django.db import models
from django.contrib.auth.models import User
class ListModel(models.Model):
owner = models.ForeignKey(User, blank=True, null=True, on_delete=models.CASCADE)
task = models.CharField(max_length=255)
completed = models.BooleanField(default=False)
my forms.py:
from django.forms import ModelForm
from .models import ListModel
from django import forms
class ListForm(ModelForm):
class Meta:
model = ListModel
fields = ['owner','task', 'completed']
You have to override the form_valid() method of the View and attach the current logged in user as an Owner.
def form_valid(self, form):
form.instance.owner = self.request.user <------ This line will do the trick.
return super().form_valid(form)
Finally got it working (my solution reference)
First, we need to exclude the owner field from the ModelForm in forms.py:
class ListForm(ModelForm):
class Meta:
model = ListModel
fields = ['task', 'completed']
# instead of above line we can simply write: exclude = ['owner']
and in the views.py:
form = ListForm(request.POST)
if form.is_valid():
task_list = form.save(commit=False)
task_list.owner = request.user
task_list.save()
return redirect('/')
where instead of task_list we can use any variable & also note that after task_list.save() no need to do form.save() because it's already included in task_list = form.save(commit=False)

Null value in column "user_id" violates not-null constraint in Django 1.9

I have exhausted all avenues in trying to put together a solution for this, but my current knowledge of Python and Django can only get me so far.
I'm creating a basic ticketing system and CreateView used to work until I created a Profile model and then separated the Ticket model into its own app. There were already a couple of tickets created when I refactored my code which is why I know ListView works, DeleteView works as well as DetailView. CreateView works until I hit the save button.
My views and models are below; I hope someone can please help me sort this out.
Ticket Model
from django.db import models
from django.contrib.auth.models import User
....
from qcapp.models import Profile
class Ticket(models.Model):
# Relations
user = models.ForeignKey(Profile, on_delete=models.CASCADE, related_name="tickets", verbose_name="user")
# Attributes
title = models.CharField(max_length=250, verbose_name="Title", help_text="Enter a Ticket Title")
color = models.CharField(max_length=7,
default="#ffffff",
validators=[RegexValidator("(^#[0-9a-fA-F]{3}$)|(^#[0-9a-fA-F]{6}$)")],
verbose_name="Color",
help_text="Enter the hex color code, like #ccc or #cccccc")
description = models.TextField(max_length=1000)
created_date = models.DateTimeField(default=timezone.now, verbose_name='Created Date')
created_by = models.ForeignKey(User, related_name='created_by_user')
# Attributes
# Object Manager
objects = managers.ProjectManager()
# Meta and String
class Meta:
verbose_name = "Ticket"
verbose_name_plural = "Tickets"
ordering = ("user", "title")
unique_together = ("user", "title")
def __str__(self):
return "%s - %s" % (self.user, self.title)
def get_absolute_url(self):
return reverse('ticket_detail', args=[str(self.id)])
Ticket View (CreateView Only)
# -*- coding: utf-8 -*-
...
from django.views.generic import CreateView, UpdateView, DeleteView
...
from .models import Ticket
...
class TicketCreate(CreateView):
model = Ticket
template_name = "tickets/ticket_form.html"
fields = ['title', 'description']
def form_valid(self, form):
form.instance.created_by = self.request.user
return super(TicketCreate, self).form_valid(form)
...
Profile Model(Imported Into Ticket Model)
from django.db import models
from django.conf import settings
from django.contrib.auth.models import User
from django.dispatch import receiver
from django.db.models.signals import post_save
from . import managers
class Profile(models.Model):
# Relations
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="profile", verbose_name="user")
# Attributes
interaction = models.PositiveIntegerField(default=0, verbose_name="interaction")
# Attributes
# Object Manager
objects = managers.ProfileManager()
# Custom Properties
#property
def username(self):
return self.user.username
# Methods
# Meta and String
class Meta:
verbose_name = "Profile"
verbose_name_plural = "Profiles"
ordering = ("user",)
def __str__(self):
return self.user.username
#receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_profile_for_new_user(sender, created, instance, **kwargs):
if created:
profile = Profile(user=instance)
profile.save()
It looks like you need to add the following to your TicketCreate class in the form_valid function:
form.instance.user = Profile.objects.get(user=self.request.user)
Let me know if that works!

__init__() got an unexpected keyword argument 'user_id'; Classed Based Views

I got the error as stated in the title above. I am trying to feed the form the user_id since my model requires that in order to add a 'table'. However, my use of get_form_kwargs seems to be problematic.
This is the model:
from django.db import models
from django.contrib.auth.models import User
class Vtable(models.Model):
user = models.ForeignKey(User)
table_name = models.CharField(max_length=200)
added_date = models.DateTimeField('date added')
class Vdata(models.Model):
table_id = models.ForeignKey(Vtable)
table_pk = models.IntegerField()
column_1 = models.CharField(max_length=200)
column_2 = models.CharField(max_length=200)
added_date = models.DateTimeField('date added')
This is the view:
from django.shortcuts import render
from django.http import HttpResponse
from django.views import generic
from vtables.models import Vtable
class CreateTableView(generic.CreateView):
model = Vtable
fields = ['table_name']
def get_form_kwargs(self):
# pass "user" keyword argument with the current user to your form
kwargs = super(CreateTableView, self).get_form_kwargs()
kwargs['user_id'] = self.request.user
return kwargs
The form class generated by a CreateView (or any model form for that matter) does not have an _id field for any foreign key. Instead, it has a field user which is a ModelChoiceField.
Furthermore, that logic should not be contained in your form. A form is merely a means of capturing and validating user input. Which user creates an object is not user input, and such logic should be in your view, e.g.:
class CreateTableView(generic.CreateView):
model = Vtable
fields = ['table_name']
def form_valid(self, form):
form.instance.user = self.request.user
return super(CreateTableView, self).form_valid(form)
In order to pass a custom value to the form, you'll have to create your own form class and pass that into the view. The default form class that the view creates doesn't know what to do with your user_id argument, and that's where the error comes from.
Here is an example on how to pass a custom form class, first the form class:
class MyForm(forms.ModelForm):
class Meta:
model = Vtable
def __init__(self, *args, **kwargs):
user_id = kwargs.pop('user_id') # Pop out your custom argument
super(MyForm, self).__init__(args, kwargs) # Initialize your form
# as usual
self.user_id = user_id # Add it as an instance variable
Then, in your view:
class CreateVTable(generic.CreateView):
form_class = MyForm
model = Vtable

Instantiating a ModelForm with initial values in CBVs

I am a Django newbie working with Django CBVs and having difficulty setting initial values for my ModelForm. To give an overview, I am trying to learn by creating a simple messaging app.
Here is my code:
models.py
import datetime
from django.contrib.auth.models import User
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
class Message(models.Model):
subject = models.CharField(_("Subject"), max_length=100)
body = models.TextField(_("Body"))
sender = models.ForeignKey(User, db_index=True, related_name='sent_messages')
recipient = models.ForeignKey(User, db_index=True, related_name='received_messages')
parent_msg = models.ForeignKey('self', related_name='next_messages', null=True, blank=True)
forms.py
from django.forms import ModelForm
from .models import Message
class MessageForm(ModelForm):
class Meta:
model = Message
exclude = ('sender', 'recipient', 'parent_msg',)
views.py
class MessageCreateView(CreateView):
form_class = MessageForm
model = Message
template_name = 'messages/compose.html'
def form_valid(self, form):
form.instance.sender = self.request.user
return super(MessageCreateView, self).form_valid(form)
urls.py
...
url(r'^compose/(?P<recipient>[\w.#+-]+)/$', MessageCreateView.as_view(), name='messages_compose_to'),
...
As you can see from the urls.py file, I am using the 'recipient' parameter as such: http://localhost:8000/members/compose/someusername
Now my problem is that I wish to open the compose message view, and initialize the recipient field by getting the username from the URL, then using the username from the url to get User with that particular username, and instantiate the form with it.
Where do I do this, in the view itself or in the form? Unless their is a better way of how to handle this.
You can add get_initial() method to return appropriate dict, something as below.
class MessageCreateView(CreateView):
...
def get_initial(self):
data = { 'recipient':
User.objects.get(username=self.kwargs.get('recipient'))
}
return data
Handle error appropriately.

Django: Accessing URL variables in Class Based Views and Forms

I'm trying to create an object in Django using the standard class based views and form libraries. Three fields in my form are dependent upon a domain variable captured from the URL pattern. My questions are:
How do I access domain within CreateSubscription so that I can set Subscription.site to Site.objects.filter(domain=domain)[0]?
How do I limit the dropdown fields rendered from CreateSubscriptionForm so that plan displays only SubscriptionPlan.objects.filter(site=Site.objects.filter(domain=domain)[0]) and payment_profile is limited to PaymentProfile.objects.filter(user=request.user)?
For clarity's sake, the domain in r'^subscribe/(?P<domain>\w+\.\w+)/$' is unrelated to my own site's domain. The django.contrib.sites framework won't help me here.
Thanks in advance for helping me untangle all of these CBV methods. :)
The URL pattern is:
from django.conf.urls import patterns, url
from accounts.views import *
url(r'^subscribe/(?P<domain>\w+\.\w+)/$',
CreateSubscription.as_view(), name='subscribe_to_site'),
)
The view in question is:
from accounts.forms import *
from accounts.models import *
class CreateSubscription(CreateView):
model = Subscription
form_class = CreateSubscriptionForm
def form_valid(self, form):
form.instance.user = self.request.user
return super(CreateSubscription, self).form_valid(form)
The relevant models are:
from django.conf import settings
from django.contrib.sites.models import Site
from django.db import models
class PaymentProfile(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL)
class SubscriptionPlan(models.Model):
site = models.ForeignKey(Site)
name = models.CharField(max_length=40)
class Subscription(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL)
site = models.ForeignKey(Site)
plan = models.ForeignKey(SubscriptionPlan)
payment_profile = models.ForeignKey(PaymentProfile)
And, finally, my form is:
from accounts.models import *
class CreateSubscriptionForm(forms.ModelForm):
class Meta:
model = Subscription
exclude = ('user', 'site', )
To access the data passed to the view, use self.args and self.kwargs
from accounts.forms import *
from accounts.models import *
class CreateSubscription(CreateView):
model = Subscription
form_class = CreateSubscriptionForm
def form_valid(self, form):
form.instance.user = self.request.user
form.instance.site = Site.objects.get(domain=self.kwargs['domain'])
return super(CreateSubscription, self).form_valid(form)
To restrict the dropdown content, you need to set the queryset for those fields. Very basically, something along these lines:
class CreateSubscriptionForm(forms.ModelForm):
plan = forms.ModelChoiceField(
queryset=SubscriptionPlan.objects.filter(xxx)
)
...
class Meta:
model = Subscription
exclude = ('user', 'site', )
This can also be done inside the view so that you have access to the domain.
class CreateSubscription(CreateView):
model = Subscription
form_class = CreateSubscriptionForm
def get_form(self, form_class):
"""
Returns an instance of the form to be used in this view.
"""
form = super(CreateSubscription, self).get_form(form_class)
form.fields['plan'].queryset = SubscriptionPlan.objects.filter(site__domain=self.kwargs['domain'])
return form
...
def form_valid(self, form):
form.instance.user = self.request.user
form.instance.site = Site.objects.get(domain=self.kwargs['domain'])
return super(CreateSubscription, self).form_valid(form)