Reputation: 14270
All pages in my Django website have a footer link "Feedback/Questions". If the new person comes to the site and clicks that link, they should be directed to a form with a pulldown to indicate if they have feedback versus a question and fields for their email address and their feedback or question. The page will have a simple header all non-authenticated users will see. On the other hand, if a site member signs in and is authenticated, they should see the same form but without the email field (since I already know their email address) and a different authenticated header containing the site's internal navbar, buttons, etc.
My initial thought was to create an abstract class FeedbackQuestion:
class FeedbackQuestion(models.Model):
submission_type = ... (type, i.e. feedback or question)
submission_text = ... (actual feedback or question)
...
class Meta:
abstract = True
Then I'd create two separate concrete child classes:
class AnonFeedbackQuestion(FeedbackQuestion):
email = models.EmailField(...)
class Meta:
db_table = anon_feedback_question
class AuthFeedbackQuestion(FeedbackQuestion):
user = models.ForeignKey(User, related_name="user")
class Meta:
db_table = auth_feedback_question
These two classes would have their own model forms:
class AnonFeedbackQuestionForm(ModelForm):
class Meta:
model = AnonFeedbackQuestion
fields = ['submission_type', 'submission_text', 'email']
class AuthFeedbackQuestionForm(ModelForm):
class Meta:
model = AuthFeedbackQuestion
fields = ['submission_type', 'submission_text']
The problem I forsee is that I will have to do the following in my view that displays the feedback form:
def get_feedback_questions(request, template):
if request.method == 'POST':
...
if request.user.is_authenticated():
form = AuthFeedbackQuestionForm(request.POST)
else:
form = AnonFeedbackQuestionForm(request.POST)
if form.is_valid():
(process form)
...
else:
if request.user.is_authenticated():
form = AuthFeedbackQuestionForm(request.POST)
else:
form = AnonFeedbackQuestionForm(request.POST)
...
context = {'form': form}
return render(request, template, context)
Having to repeat these if/then/else blocks to identify which form to use seems rather inelegant. Is there a better, cleaner "Django" way to do this?
Thanks!
Upvotes: 2
Views: 453
Reputation: 28662
I wouldn't subclass your models - if it's an anonymous question you could just include a user
attribute as well as an email
attribute on one model with blank=True
and null=True
:
class FeedbackQuestion(models.Model):
submission_type = ... (type, i.e. feedback or question)
submission_text = ... (actual feedback or question)
email = models.EmailField(..., blank=True, null=True)
user = models.ForeignKey(User, related_name="user", blank=True, null=True)
...
class Meta:
abstract = True
This way you can add either the email
for an anonymous user's feedback/question or the user
if they're authenticated.
Then I'd combine your forms into one including the email field, but remove the email field depending on if the user is authenticated (see this answer):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
super(UserForm, self).__init__(*args, **kwargs)
if self.user:
# For logged-in users, email field not necessary
self.fields.pop('email')
else:
# Otherwise, the field needs to be required
self.fields['email'].required = True
Then you just need to make sure you create the user appropriately as you clean the form's data (e.g., make sure the email address isn't already taken, etc.)
Upvotes: 1