How to save two related entries in a single form - python

I am new to Django and I am creating a simple 2 page messageboard app (submit page and messageboard page)
I am struggling with the form for my submit page. As I am learning my way around Django I decided not to use the standard user model and opted to rather create a model (Poster) which has a one to one relationship with the message model.
Basically in one form I would like to add a message and a poster(foreign key) which has multiple fields.
Is it possible to achieve what I am trying to do?
Thanks in advance for the help.
I don't really know what to try or what to look for. I have included some code below.
Models
class Poster(models.Model):
full_name = models.CharField(max_length = 50)
phone_number = models.CharField(max_length = 15)
email = models.EmailField()
class Message(models.Model):
message_text = models.CharField(max_length=10000)
created_at = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(Poster, on_delete=models.CASCADE)
class MessageForm(forms.ModelForm):
class Meta:
model = Message
fields = ['full_name', 'phone_number', 'email', 'message_text']

Your mistake is trying to use a ModelForm subclass which is for creating or updating one object (database row) only.
Use a plain forms.Form with the fields you want. You'll have to explicitly code them as CharField, EMailField, etc. Then in form_valid (assuming your view is a FormView) you will do something like
poster = Poster()
poster.full_name = form.cleaned_data['full_name']
# ditto for phone_number and email
poster.save()
message = Message( user=poster,
message_text = form.cleaned_data['message_text'] )
message.save()

Related

Custom user model doesn't create objects

I have created a CustomUser model and it has a one to relationship with my two other models.
class User(AbstractUser):
is_learner = models.BooleanField(default=False)
is_teacher = models.BooleanField(default=False)
class Teacher(models.Model):
#teacher_name = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, primary_key=True)
class Learner(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, primary_key=True)
They do not show up when I try to create learner and teacher objects in their respective databases, as shown:
class LearnerSignUpForm(UserCreationForm):
email = forms.EmailField()
class Meta(UserCreationForm.Meta):
#User = CustomUser
model = CustomUser
fields = ["username", "email", "password1", "password2"]
#fields = UserCreationForm.Meta.fields + ("username", "email", "password1", "password2")
#transaction.atomic
def save(self, *args, **kwargs):
user = super().save(commit=False)
user.is_learner = True
user.save()
learner = Learner.objects.create(user=user)
return user
How do I get it to save in learner and teacher tables respectively?
Welcome to StackOverflow!
It is worth pointing that using model.objects.create(...) you're implicitly telling Django that you want to create the object with the specific values you give and save it. check the docs page here
If you wish to save an object for which has associated items, you can create a form and use a ModelChoiceField and provide the model and default choices using the choices kwarg. An example of a choices value would be YourUserModel.objects.all() However, I think in your case you may not want to give the user the freedome to make this choice, in which it would be correct to override the save() method of your form and create the intended logic.
Secondly, I have used Django for quite some time and never seen a Model.Meta class used in this way so forgive me if I'm mistaken, but I also think you need have your save() method directly on your LearnerSignupForm and not on the Meta class.
Thirdly, if you setup your forms correctly, using the correct types of fields, Django forms will deal with all the messy stuff for you and complain at you when you do something wrong (usually gracefully).
Lastly, I would highly recommend having a read through the docs page for creating new objects
Free code:
class LearnerSignUpForm(UserCreationForm):
email = forms.EmailField()
class Meta(UserCreationForm.Meta):
model = CustomUser
fields = ["username", "email", "password1", "password2"]
#transaction.atomic
def save(self, *args, **kwargs):
user = super().save(commit=False)
user.is_learner = True
# create the relationship between the user and learner
learner = user.learner_set.create(user=user)
# user.learner_set.add(learner) # usually this way with FK
user.save()
return user # should this return the Learner object?
One final bit of advice:
Try to make your code as readable as possible! There's no reason that code can't be functional and beautiful! And where possible, you should make you class names and variable names as appropriate as possible, I would for example maybe use Student instead of Learner

How can I get the data of the modelform?

I am building a user model and wanna attach it to a modelform as below shown. How can I get the email of each user by shell accessing the mysql database?
I have tried these to get the data by shell, but the previous one said the object has no email attribute and the latter one said the forms.py has no object.
from Project.User.models import UserProfile as p
p.objects.filter(is_active=True).first().email //
from Project.User.forms import ClientProfileForm as p
p.objects.filter(is_active=True).first().email
Code:
models.py:
class UserProfile(BaseModel):
user = models.OneToOneField(User, primary_key=True, on_delete=models.CASCADE, verbose_name='client')
name = models.CharField('name',max_length=30, null=False, help_text="Name")
gender = models.CharField('sex',max_length=1, help_text="Sex")
class Meta:
verbose_name = 'Client'
verbose_name_plural = 'Client'
def __str__(self):
return 'client:%s' % self.user
forms.py:
class ClientProfileForm(forms.ModelForm):
email = forms.EmailField()
class Meta:
model = UserProfile
fields = ("user", "name", "gender")
Have you tried using
p.objects.filter(is_active=True).first().user.email
There is no data in form before you send a data to it so you can't get the user and it's email from your shell.
Forms are used to get data from HTML templates for example and validate that data and then add those data to the database.
If you fill a form, you can access the data that has been cleaned from your view like this:
form = ClientProfileForm(request.POST)
if form.is_valid():
print(form.cleaned_data['my_form_field_name'])
Also you can set some default values for forms. Example here:
Django forms initial example on stackoverflow
And finally for your problem:
You are trying to access email from UserProfile which doesn't have a email field but the User model does.
so you can access the email like this:
from Project.User.models import UserProfile as p
# Get the first profile and then get the email from user model
p.objects.filter(is_active=True).first().user.email
from Project.User.forms import UserProfile as p
This line is incorrect because the name of your form is ClientProfileForm and not UserProfile. This is why you are getting the error forms.py has no object

How to save choices value in django ?

I am donig with a poll system for my class. I use model-form and create-view to serve the poll form. I use choices in in the field but I just find out that create-view only save the last value of the checkboxes and I want to save all the selected choices as a list maybe. I've tried to change the form_valid() method, but I just find out that I need to iterate all the fields to check wheather there are multipule choices. It's not flexible. And I can't figure out other solutions...
How can I meet this requirement? I am truly a newbie..
Thanks in advance.
Thank the friend below for replying in such a short interval after I raised my question. Here is my code.
models.py
CHOICES = (('m','Math'),('f','French'),('s','Science'),('l','literature'))
class Poll(models.Model):
[...]
subject = models.CharField(max_length = 5,choices = CHOICES, blank=True)
[...]`
forms.py
class PollForm(forms.ModelForm):
model = Poll
fields = [..., 'subject', ...]
widgets = {'subject':forms.CheckboxSelectMultiple}
views.py
class PollView(CreateView):
form_class = PollForm
template_name = 'poll.html'
Students can choose subjects they want.
It seems like you need to convert your model. If you could provide a sample of the structure that you are using it would be helpful. Still lets try solving your query. First you need identify that choices is nothing more than a many to many field. Saving it in the db should be a bit easier that way. Lets try taking an example with choices for a user:
class Choices(models.Model):
description = models.CharField(max_length=100)
class UserProfile(models.Model):
user = models.ForeignKey(User, blank=True, unique=True, verbose_name='profile_user')
choices = models.ManyToManyField(Choices)
def __unicode__(self):
return self.name
Now if you want to make a default form you could simply do something like:
class ProfileForm(forms.ModelForm):
Meta:
model = UserProfile
Now comes your main view. This can be editted and rendered to whatever your use case demands it to be:
if request.method=='POST':
form = ProfileForm(request.POST)
if form.is_valid():
profile = form.save(commit=False)
profile.user = request.user
#Implement this as a pre-save so that you can add additional value
profile.save()
else:
form = ProfileForm()
Hope this helps.

Multiple User Types In Django

I am new to Django and trying to create an App with two User Types (Freelancers and Customers). I understand how to create a User profile Class and it works well for me:
class UserProfile(models.Model):
user = models.OneToOneField(User)
description = models.CharField(max_length=100, default='')
country = models.CharField(max_length=100, default='')
website = models.URLField(default='')
phone = models.IntegerField(default=0)
def create_profile(sender, **kwargs):
if kwargs['created']:
user_profile = UserProfile.objects.create(user=kwargs['instance'])
post_save.connect(create_profile, sender=User)
This works well for me on a one user type user. But now I am building an app with 2 types of users (freelancers and customers), what is the best approach to get this done. Both users will have different view and info. Should I:
Create 2 different apps, and repeat the normal registeration and login for each.
If I do the above, hope the freelancers when logged in won't access customers view.
How do I add user type to the user profile if I decide to use one app and model for it.
Please I need a step by step beginner approach, or a link to relevant source.
Thanks.
You could try this:
class UserProfile(models.Model):
user = models.ForeignKey(User)
#define general fields
class Freelancer(models.Model):
profile = models.ForeignKey(UserProfile)
#freelancer specific fields
class Meta:
db_table = 'freelancer'
class Customers(models.Model):
profile = models.ForeignKey(UserProfile)
#customer specific fields
class Meta:
db_table = 'customer'
You can then have as many Users as you want from the UserProfile.
You should need just use Groups Django mechanism - you need to create two groups freelancer and let say common and check whether user is in first or second group - then show him appropriate view
To check whether user is in group you can use
User.objects.filter(pk=userId, groups__name='freelancer').exists()
You Could Try extending the Default Django Auth User like this
Create an App with Account or Whatever name you like , then in models.py write like below
class User(AbstractUser):
is_head = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
is_public = models.BooleanField(default=False)
Add Auth Extended Model in Settings.py
AUTH_USER_MODEL = 'accounts.User'
Migrate your Account app and you are all set with Your User Extended Model.

Django forms, saving non-user submitted data

class SomeModel(models.Model):
text = models.TextField()
ip = models.IPAddressField()
created_on = models.DateTimeField()
updated_on = models.DateTimeField()
Say I have that as a model, what if I wanted to only display 'text' field widget for the user to submit data, but I obviously wouldn't want the user to see the widgets for ip,created_on, updated_on to be changed. Yes I know I can add it as a hidden field for the form, but that's not what I'm looking for.
I'm more wondering how can I not render those fields at all when rendering my form, and just dynamically populating the fields when a form is posted and pass form validation? I'm guessing to somehow override the blank values of ip,created_on,updated_on while the form is being cleaned/validated. I know how to do this in the view by using request.POST.copy and injected my values, but I'd like to know if it's possible in models or forms.
Two things:
First ModelForms:
Class SomeModelForm(ModelForm):
class Meta:
exclude = ['ip','created_on', 'updated_on']
Two Model Fields API:
class SomeModel(models.Model):
text = models.TextField()
ip = models.IPAddressField()
created_on = models.DateTimeField(auto_now_add=True)
updated_on = models.DateTimeField(auto_now=True)
Three:
For ip, i think you should to that in your views.

Categories