Django: Saving form with User as Foreign key - python

I have a problem and I'm looking for the answer like a week ago without finding any.
I want to save a model, using the Django User as a foreign key, I tried creating a new user like this:
user=User.objects.create_user(username, email, password)
And then put the entire user or the user id and does not work, just like this:
studio_form.user_id=user
studio_form.user_id=user.id
And nothing happens, everything is saved in the database, the user, the group, but not the user as a foreign key, the foreign key user_id is null every single time, help please.
so, this is my code:
Django Model:
from django.contrib.auth.models import User
class Studios(models.Model):
user_id=models.ForeignKey(User)
studio_name=models.CharField(max_length=250)
Django Forms:
from django import forms
from web.models import *
class studioForm(forms.ModelForm):
class Meta:
model = Studios
exclude=('user_id')
widgets = {
'password': forms.PasswordInput(),
}
Django View:
def studio_register(request):
if request.method == "POST":
studio_form=studioForm(request.POST, request.FILES)
if studio_form.is_valid():
studio_form.save(commit=False)
data=request.POST
username=data.get("user_name")
email=data.get("email")
password=data.get("password")
user=User.objects.create_user(username, email, password)
group=Group.objects.get(name='studio')
group.user_set.add(user)
studio_form.user_id=user.id
studio_form.save()
Any idea?

You are passing your data back to the studio_form here:
def studio_register(request):
# other view code
studio_form.user_id=user.id
studio_form.save()
You actually need to pass the data to the Studios model in order to save it to the database. The Form is only for cleaning data and temporary holding it until you access the model to save the data to the database.
So if here is you model:
from django.contrib.auth.models import User
class Studios(models.Model):
user_id=models.ForeignKey(User)
studio_name=models.CharField(max_length=250)
You need access it in your view:
def studio_register(request):
# other view code
studio = Studios.objects.create(user_id=user, studio_name=name)
That will create a Studios object, saving the data to the database.
user_id needs to be a User object (not the user.id) since it is a foreign key to the User model. So once you get the user object, then just set the user_id in your Studios model equal to the user. A better naming convention, however, would be to name the field "user" (in order to avoid confusion):
class Studios(models.Model):
user = models.ForeignKey(User)
Make sure that you save studio_name as well because it is a required field in your Studios model.

I've been doing it the other way around user.groups.add(group) maybe that will work for you?

Related

Django - when user submits a form, tables are created, but the connection between the tables is not

i am working on a small project where a customer can go to my website and register phone numbers associated to their name. it is a connection-wise registration (where number1 <--> number2 is one connection), so only two phone numbers are ever registered per session. when viewing a particular phone number (in admins panel, for instance), i want to see the id of the user it belongs to and the id of the connection it is part of. When i view a connection the same way, i want to see who registered that particular connection, and the two phone numbers that were registered in the same session.
my problem: all of the foreign keys give a "django.db.utils.IntegrityError: NOT NULL constraint failed" error. I had to set null=True for all of the foreign keys for the customer's data to be submitted at all. the ManyToManyField doesn't give any bugs - but it doesn't limit each Connection to only the two phone numbers registered in the session like it should.
and so, this kind of structured connection outlined above isn't made when the data is added. it's like each phone number and each connection and each user becomes its own little island, without ties to the other tables. when i click into 'connections' in the admin panel, each connection that's been made is listed there, but when i select it, i see all the phone numbers ever registered. it's the same for every single connection i choose. and when i select a phone number, there's nothing that connects it to the user who added it, or the 'connection that it's part of.
my models.py file:
from django.db import models
class Customer(models.Model):
customer_name = models.CharField(max_length=100)
customer_email = models.CharField(max_length=100)
class Phonenumber(models.Model):
customer_number = models.CharField(max_length=100)
belongs_to = models.ForeignKey(Customer, on_delete=DO_NOTHING)
class Connections(models.Model):
registered_at = DateTimeField(auto_now=True)
got_number_at = DateTimeField(default=None) # this becomes the date the connection was made
registered_by = models.ForeignKey(Customer, on_delete=DO_NOTHING)
phonenumber_id = models.ManyToManyField(Phonenumber)
my phonebook_form.py file:
from django.forms import ModelForm
from django import forms
from .models import *
class CustomerForm(ModelForm):
class Meta:
model = Customer
fields = ['name', 'email']
widgets = {
'name': TextInput(attrs={'class':'form-control', 'id': 'cus_name'})}
'email': TextInput(attrs={'class':'form-control', 'id': 'cus_email'})}
class NumberForm(ModelForm):
class Meta:
model = Phonenumber
fields = ['customer_number']
widgets = {
'customer_number': TextInput(attrs={'class':'form-control', 'id': 'cus_number'})}
class ConnectionForm(ModelForm):
class Meta:
model = Connections
fields = ['got_number_at']
widgets = {
'got_number_at': TextInput(attrs={'class':'form-control', 'id': 'connection_date'})}
my views.py file:
from django.shortcuts import render
from .phonebook_form import *
from1 = CustomerForm()
form2 = NumberForm()
form3 = ConnectionForm()
if request.method = "POST":
form_customer = CustomerForm(request.POST)
form_number = NumberForm(request.POST)
form_connection = ConnectionForm(request.POST)
if form_customer.is_valid():
form_customer.save()
if form_number.is_valid():
form_number_save()
if form_connection.is_valid():
form_connection.save()
context = {'form1': form1,
'form2': form2,
'form3': form3
}
return render(request, 'phoneBook/phonebook.html')
i've also added a picture of the relational schema of the database:
i don't even know where to start to fix this issue. i've watched so many django website builder tutorials and read so many articles, and all of them seem to do it exactly the same way without any issues. What am i missing? (though i've been using python for 2 years now, i'm a complete beginner in Django (1 week) and web development in general, so the simpler the fix the better)
Your forms will check if the phone number is correct (the fields of your form), and if that is the case save a record in the database, but it will not link to items that happen to be saved in the same view. It is not said that you want to link these object in any way, or perhaps there are a lot of ways to connect this. Forms will thus not look what another form constructed. The programmer will have to implement the linking logic.
We can first check if all forms are valid, and then use the object wrapped in one form to set it to the object wrapped in another form, like:
if form_customer.is_valid() and form_number.is_valid() and form_connection.is_valid():
customer = form_customer.save()
form_number.instance.belongs_to = customer
phone_number = form_number_save()
form_connection.instance.registered_by = customer
connecion = form_connection.save()
connection.phonenumber_id.add(phone_number)
Note: In case of a successful POST request, you should make a redirect
[Django-doc]
to implement the Post/Redirect/Get pattern [wiki].
This avoids that you make the same POST request when the user refreshes the
browser.

Django - Use TextInput form to look up a ForeignKey

I'm currently trying to build an application in Django where users should be able to create new entries for a model. Said model contains a ForeignKey relation to a different model. However, the classic dropdown form field is not practical, because there could be hundreds of entries to choose from. Instead, I'd like to allow users to simply type in the name of the entry (all names are unique), which then gets transformed to the corresponding ID when pressing the Submit button.
I feel like this should be pretty straightforward to do, but since I'm new to Django I'm having a hard time to figure out how to do it. Currently the problem is, that the form field is declared invalid, due to the ForeignKey not existing (since the user typed in the name and not the ID of the database entry).
Code example:
forms.py:
class DogForm(ModelForm):
class Meta:
model = Dog
fields = "__all__"
widgets = {" shelter": widgets.TextInput(attrs={"id":" shelter", "placeholder":" Dog Shelter"}),
"name": widgets.TextInput({"id":"name", "placeholder":"Name"}),
def clean(self):
shelter= self.data.get("shelter")
self.cleaned_data["shelter"] = Shelter.objects.get(name=shelter)
return self.cleaned_data
views.py:
class DogCreate(CreateView):
model = Dog
form_class = DogForm
def form_valid(self, form):
if form.is_valid():
instance = form.save(commit=False)
instance.save()
return super(DogCreate, self).form_valid(form)
As you can see, my idea was to overwrite the clean method, by adding the correct Shelter to self.cleaned_data (based on the name the user put into the TextInputField). But this does not seem to work, because when pressing submit, the form is still declared invalid.

user profile with django

What would be the steps to create a user profile other than the administrator user please, I am a newbie. I leave the model class of which I want to be a user profile
from django.contrib.auth.models import User
user = models.OneToOneField(User, ondelete=models.CASCADE)
Add this field to the model.
If you are using Django's auth user registration form then, while registering new user in views.py:
form = UserRegistrationForm(request.POST)
if form.is_valid():
instance = form.save()
apoderado = Apoderado()
apoderado.user = instance
apoderado.other_fields = form.cleaned_data['other_fields']
apoderado.save()
This will create new user with extra fields. This is a simple trick but, if any error occurs then only half of data will be stored. If you want to go for extra, use Django signals.

Prevent users to access data of another user when typing the slug in the url

If user 1 creat this ticket :
mywebsite/manager/tickets/ticket-from-user-1/
And user 2 create that :
mywebsite/manager/tickets/ticket-from-user-2/
How can I prevent user 1 to access the ticket from user 2 or other users by typing it in the url?
views.py
class TicketDisplay(LoginRequiredMixin, DetailView):
model = Ticket
template_name = 'ticket_detail.html'
context_object_name = 'ticket'
slug_field = 'slug'
def get_context_data(self, **kwargs):
context = super(TicketDisplay, self).get_context_data(**kwargs)
context['form_add_comment'] = CommentForm()
return context
url.py
url(r'^manager/tickets/(?P<slug>[-\w]+)/$',views.TicketDetail.as_view(), name='ticket_detail')
I recently implemented this functionality in a project. It can be done by using automatically generated uuid's. Django has a built-in model field for this, or you can use a slug field and give it a default value. Here is a quick example.
In your models.py file, import the uuid library and then set the default value of your slug field to be uuid.uuid4.
models.py:
import uuid
class Ticket(models.Model):
uuid = models.SlugField(default=uuid.uuid4, editable=False)
...
In urls.py, just use the uuid field as if it were a pk. Something like this:
url(r'^manager/tickets/(?P<uuid>[0-9a-z-]+)/?$', TicketDetail.as_view(), name='ticket-detail'),
In your detail, update, and delete views, you will need to make sure and set these two attributes so that Django knows which field to use as the slug:
slug_field = 'uuid'
slug_url_kwarg = 'uuid'
Then in your templates and whenever you need to retrieve an object for the kwargs, just use the uuid instead of the pk.
Note that in addition to this, you should also do all you can with permissions to block users from seeing other pages. You may be able to block certain accounts from viewing other peoples details. For instance, you could probably write a permissions mixin to check whether request.user matches up with the object that the view is handling.
tldr This is assuming that you have some kind of relation to a user on your Ticket model:
class SameUserOnlyMixin(object):
def has_permissions(self):
# Assumes that your Ticket model has a foreign key called user.
return self.get_object().user == self.request.user
def dispatch(self, request, *args, **kwargs):
if not self.has_permissions():
raise Http404('You do not have permission.')
return super(SameUserOnlyMixin, self).dispatch(
request, *args, **kwargs)
Finally, stick it on to your view like this:
class TicketDisplay(LoginRequiredMixin, SameUserOnlyMixin, DetailView):
...
You will need to make user 1 have something that user 2 can not imitate.
Preferred way would be to use your existing auth methods and check if user accessing the page is allowed to do so.
If you don't have registration on your site then you could generate some random string - secret - and store it with the question. If 'user' has that secret then he's allowed.
This secret string can be stored in cookie or made a part of URL.
Storing it in a cookie has a drawback: if cookie is lost then nobody can access the page. Also user cannot access it from other browser.
Making it a part of url has another drawback: if someone else sees the link, he can access that page too. This could be bad if user's software automatically reports likns he visits somewhere.
Combining these approaches has both drawbacks.

Add extra fields to my UserCreationForm from my extended User model

I need two extra fields for the user data so I followed the official django docs Extending the existing User model, the admin form for users works fine but I have a UserCreationForm and I want to add the two extra fields in that form too, already tried to use two forms, the UserCreationForm and the form for my extended user model but I can't get the id of the UserCreationForm to fill the user_id of my extended user model so I search how to use a signal to do that like the django docs recommend and find this Django Signals: create a Profile instance when a new user is created but that only fill the user_id of my extended user model that is the OneToOneField but not the two extra fields.
sorry for my bad english.
I need to run but here's a quick implementation. It needs some tweaks apparently but should get you started:
# this is your model form for extended OneOnOne with user
class ExtendedForm(forms.ModelForm):
model = ExtendedModel
# temporary exclude user field to pass the validation
exclude = ('user')
def create_user(request):
user_form = UserForm(request.POST or None)
extra_form = ExtendedForm(request.POST or None)
if user_form.is_valid() and extra_form.is_valid():
# create a new user first
new_user = user_form.save()
# create an object in memory but not save it
new_extended_obj = extra_form.save(commit=False)
# assign the user to the extended obj
new_extended_obj.user = new_user
# write to database
new_extended_obj.save()

Categories

Resources