How save User data in OneToOneField | Django? - python

I have two models(User and Profile). In Profile model as you can see I have idx field which I use for sorting. In my project I show list of profiles and admin can sorting them by drag and drop. For thats why I use idx. Also this field is invisible and admin dont change its value manually.
Question: How edit User data by profile id. As I said before I show list of profiles. Right now I have edit form where I show user data by profile id. Problem raise when I try to submit form after changes.
models.py:
# -*- coding: utf-8 -*-
from django.db import models
from django.contrib.auth.models import User
from django.dispatch import receiver
from django.db.models.signals import post_save, post_delete
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
idx = models.IntegerField(verbose_name='Field for sorting, default=0, blank=True,)
class Meta:
ordering = ['idx', 'pk']
db_table = 'user_profile'
#receiver(post_save, sender=User)
def create_or_update_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
instance.profile.save()
#receiver(post_delete, sender=Profile)
def delete_user(sender, instance=None, **kwargs):
try:
instance.user
except User.DoesNotExist:
pass
else:
instance.user.delete()
forms.py:
from django import forms
from django.contrib.auth.models import User
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = '__all__'
views.py:
class UserEditView(UpdateView):
template_name = 'users/edit_user.html'
form_class = UserForm
model = User
def get(self, request, *args, **kwargs):
data = dict()
profile = Profile.objects.get(pk=self.kwargs['pk'])
user_edit_form = UserForm(instance=profile.user)
context = {
'profile': profile,
'user_edit_form': user_edit_form
}
data['html_user_edit_form'] = render_to_string(
'users/edit_user.html', context, request=request
)
return JsonResponse(data)
def form_valid(self, form):
form.save()
data = dict()
data['form_is_valid'] = True
context = {'users': User.objects.all()}
data['html_users'] = render_to_string('users/users.html', context)
return JsonResponse(data)
ERROR:
LevelName: WARNING | Module: base | Process: 662 | Thread: 123145371320320 | Message: Not Found: /user/31/edit/
LevelName: WARNING | Module: basehttp | Process: 662 | Thread: 123145371320320 | Message: "POST /user/31/edit/ HTTP/1.1" 404 1800

Finally I found my mistake.
From url I take id of profile but then in template I need to send id of user.
I just add context = {'user': profile.user,} and then use in template user.id instead of profile.id.

Related

Set and not let modify foreign key value (user)

I have 2 models that I want to preset the value and ideally have it hidden from the Django admin when creating new record, this way the user don't amend this value. This are the created and modified by that are foreign keys to users.
I found this link https://pypi.org/project/django-currentuser/, that i thought it might do the job, however it reverted my django from the latest version to version 3, so I dont want to use it, and also, it doesnt work if i set either created or last modified but not both, if i set it in the 2 models i get 4 errors.
I am wondering if there is an easy way to set this default value?
from django.db import models
from email.policy import default
from django.contrib.auth.models import User
from django.utils import timezone
# from django.contrib import admin
# https://pypi.org/project/django-currentuser/
from django_currentuser.middleware import (get_current_user, get_current_authenticated_user)
from django_currentuser.db.models import CurrentUserField
class Company(models.Model):
modified_by = models.ForeignKey(User, related_name='company_modified_by', unique = False, on_delete=models.CASCADE)
created_by = CurrentUserField()
created_date = models.DateTimeField(auto_now_add=True)
modified_date = models.DateTimeField(auto_now_add=True)
name = models.CharField(max_length=150, unique = True)
class Meta:
verbose_name = "Company"
verbose_name_plural = "Companies"
def __str__(self):
return self.name
class UserProfile(models.Model):
modified_by = models.ForeignKey(User, related_name='user_profile_modified_by', unique = False, on_delete=models.CASCADE)#CurrentUserField(on_update=True)
created_by = CurrentUserField()
created_date = models.DateTimeField(auto_now_add=True)
modified_date = models.DateTimeField(auto_now_add=True)
I've learned that instance doesn't work as described in a previous stackoverflow interaction. I've done some tinkering figured out how to do my usual is_edit flag in the admin
This is what I've come up with. It requires changing the admin.py and adding a new form.
The values will still show up in that table in the admin page, which I assume is good, they're just hidden in the new+edit forms.
Note: I only did Company as I'm not 100% sure on how UserProfile works as the only two fields are supposed to be hidden ones, so what's to edit?
admin.py
from django.contrib import admin
# this \/ needs to change
from myapp.forms import CompanyForm
from myapp.models import Company
class CompanyAdmin(admin.ModelAdmin):
# columns to show in admin table
list_display = (
'name',
'created_by', 'created_date',
'modified_by', 'modified_date',
)
# custom form
form = CompanyAdminForm
# override default form_save to pass in the request object
# - need request.user inside the save method for `{x}_by = user`
def save_form(self, request, form, change):
return form.save(commit=False, request=request)
admin.site.register(Company, CompanyAdmin)
forms.py
from django import forms
from django.contrib import admin
# this \/ needs to change
from myapp.models import Company
class CompanyForm(forms.ModelForm):
class Meta:
model = Company
fields =['name']
exclude =['modified_by', 'created_by', 'created_date', 'modified_date']
def __init__(self, *args, **kwargs):
# We must determine if edit here, as 'instance' will always exist in the save method
self.is_edit = kwargs.get('instance') != None
super(CompanyForm, self).__init__(*args, **kwargs)
def save(self, commit=True, *args, **kwargs):
# We must Pop Request out here, as super() doesn't like extra kwargs / will crash
self.request = kwargs.pop('request') if 'request' in kwargs else None
obj = super(CompanyForm, self).save(commit=False, *args, **kwargs)
# do your stuff!
from datetime import datetime
if self.is_edit:
obj.modified_date = datetime.now().date()
obj.modified_by = self.request.user
else:
obj.created_date = datetime.now().date()
obj.created_by = self.request.user
if commit:
obj.save()
return obj
Note: But you can reuse the Company form for a non-admin form, you just have to remember to call the save like: form.save(commit=False, request=request)
# Example (Might need some minor tinkering)
def myview(request):
if request.method == 'POST':
form = CompanyForm(request.POST)
if form.is_valid():
form.save(commit=True, request=request)
Normally I inject vars into the declaration + __init__, ex form = CompanyForm(request.POST, request=request, is_edit=True) instead of the save() but 1 look at contrib/admin/options.py + ModelAdmin.get_form() & no thanks!
You can use editable=False, eg,
modified_by = models.ForeignKey(User, related_name='company_modified_by', unique = False, on_delete=models.CASCADE, editable=False)
According to the docs, If False, the field will not be displayed in the admin or any other ModelForm. They are also skipped during model validation. Default is True.
That way, you can set it programmatically during creation (eg, via a request to a view) and not have to worry about it being edited.
def create_view(request):
if request.method == "POST":
company_form = CompanyForm(request.POST)
company_form.instance.created_by = request.user
company_form.save()
(Also - don't forget, use
modified_date = models.DateTimeField(auto_now=True)
for modified dates. auto_now_add is for one time creation updates.)

Django admin save and display data only for the logged in user

In my django admin project i have to save and then display data for different user, i would that when i save automatically field user take the loged in user value and when i re logged in i can see only the data saved for my user only.
I have this kind of models.py:
class TheLinks(models.Model):
lk_name = models.CharField(max_length=20)
def __str__(self):
return self.lk_name
class UserComLinks(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="User")
l_id = models.ForeignKey(TheLinks, on_delete=models.CASCADE, verbose_name="Portal")
l_link = models.CharField(max_length=100)
def __str__(self):
return str(self.l_id)
now if i run my django admin i see this form:
well, first i would to hide the username field and make my model save in automatic this data using the current logged in user then i would that when the user logged in can see only hos data.
I try to manage admin.py in this kind of fashion:
admin.py
class UserComLinksAdmin(admin.ModelAdmin):
list_display = ('l_id', 'l_link')
def changeform_view(self, request, obj_id, form_url, extra_context=None):
try:
l_mod = UserComLink.objects.latest('id')
except Exception:
l_mod = None
extra_context = {
'lmod': l_mod,
}
return super(UserComLinksAdmin, self).changeform_view(request, obj_id, form_url, extra_context=extra_context)
but nothing change.
Can someone pleas help me about?
So many thanks in advance
You can exclude the user field from the admin form and set the value of current user before saving:
class UserComLinksAdmin(admin.ModelAdmin):
list_display = ('l_id', 'l_link')
exclude = ('user',)
def save_form(self, request, form, change):
obj = super().save_form(request, form, change)
if not change:
obj.user = request.user
return obj

Django Modelform doesn't accept selection on POST

The dropdown list appears correctly in the html, However I am unable to figure out why I run into the same error time after time when I try to submit / .
"Select a valid choice. That choice is not one of the available choices."
the problem context
I have two models defined in Django. One CourseModel database to hold all the offered courses and one registration database to link a course to a user.
models.py
from django.db import models
# Create your models here.
class CourseModel(models.Model):
course = models.CharField(max_length=100)
date = models.DateField(max_length=100)
time = models.TimeField()
location = models.CharField(max_length=100)
datetime = models.DateTimeField()
class RegistrationModel(models.Model):
name = models.CharField(max_length=100)
adress = models.CharField(max_length=100)
city = models.CharField(max_length=100)
email = models.EmailField(max_length=100)
course = models.ForeignKey('self', on_delete=models.CASCADE)
def __str__(self):
return self.name
I use modelForm to create a registration form, where the user can subscribe for a course from a dropdown list.
forms.py
from django.forms import ModelForm, RegexField
from home.models import RegistrationModel, CourseModel
from django import forms
import datetime
class RegistrationForm(ModelForm):
def __init__(self, *args, **kwargs):
super(RegistrationForm, self).__init__(*args, **kwargs)
self.fields['course'].queryset = CourseModel.objects.exclude(date__lt=datetime.datetime.today()).values_list('datetime', flat=True)
self.fields['course'].empty_label = None
class Meta:
model = RegistrationModel
fields = '__all__'
views.py
from django.shortcuts import render, redirect
from home.forms import RegistrationForm
from .models import CourseModel
import datetime
def home(request):
return render(request, 'home/home.html')
def registration(request):
if request.method == 'POST':
form = RegistrationForm(request.POST)
crs = request.POST.get('course')
print(crs)
if form.is_valid():
cleanform = form.save(commit=False)
cleanform.course = crs
cleanform.save()
return redirect('home')
else:
form = RegistrationForm()
return render(request, 'home/registration.html', {'form': form})
In the RegistrationForm's __init__() method, your self.fields['course'].queryset = ...values_list('datetime', flat=True) returns datetime instances. See values_list() docs.
I believe this may cause the issue. I guess the queryset should return CourseModel instances, based on the Django docs:
ForeignKey is represented by django.forms.ModelChoiceField, which is a ChoiceField whose choices are a model QuerySet.
Also, your RegistrationModel.course field has a foreign key to 'self' instead of the CourseModel. Not sure if that is what you want.
Other examples of setting the field queryset can be found here.

custome user creation in django fields not storing value

I am trying to make a user registration form in django.
I browsed through many links but I am still confused. I am making some sill mistake please point it out.
here is my code:
models.py
from django.db import models
from django.db.models.signals import post_save
from django.contrib.auth.models import User
class UserProfile(models.Model):
mobile = models.CharField(max_length = 20, null=False)
address = models.CharField(max_length = 200)
user = models.OneToOneField(User, unique=True)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
post_save.connect(create_user_profile, sender=User)
forms.py
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
class CustomerRegistrationForm(UserCreationForm):
mobile = forms.CharField(max_length = 20)
address = forms.CharField(max_length = 200)
class Meta:
model = User
fields = ('username','email','mobile','address','password1','password2')
view.py
from django.shortcuts import render_to_response
from django.http import HttpResponseRedirect
from django.template import RequestContext
from django.core.context_processors import csrf
from neededform.forms import CustomerRegistrationForm
def register(request):
print "I am in register function"
if request.method == 'POST':
if request.method == 'POST':
form = CustomerRegistrationForm(request.POST)
if form.is_valid():
f = form.save()
return HttpResponseRedirect('/registered/')
else:
args = {}
args.update(csrf(request))
args['form'] = CustomerRegistrationForm()
return render_to_response('User_Registration.html', args ,context_instance = RequestContext(request))
what I am thinking is that when I do a form.save() in views.py, django should create the user in auth_user table and must insert the values (i.e mobile and address ) in the UserProfile table also.
but what happening is that it is inserting data in auth_user table correctly but in the UserProfile table only id and user_id coloumns are filled, mobile and address both remains empty.
What am I doing wrong ? What else must be done ?
Thank you.
Take a look at the following:
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
You create a UserProfile object which only has its user attribute set!
I don't think that using signal is the best approach to your problem since it's not easy to pass the mobile and address from your form to the Profile creation point. Instead you can override the save() method of your CustomerRegistrationForm where you'd first save the user and then create the profile. Something like this:
class CustomerRegistrationForm(UserCreationForm):
# rest code ommited
def save(self, commit=True):
user = super(CustomerRegistrationForm, self).save()
p = UserProfile.objects.get_or_create(user=user )
p[0].mobile = self.cleaned_data['mobile']
p[0].address = self.cleaned_data['address']
p[0].save()
return user

How to auto insert the current user when creating an object in django admin?

I have a database of articles with a
submitter = models.ForeignKey(User, editable=False)
Where User is imported as follows:
from django.contrib.auth.models import User.
I would like to auto insert the current active user to the submitter field when a particular user submits the article.
Anyone have any suggestions?
Just in case anyone is looking for an answer, here is the solution i've found here:
http://demongin.org/blog/806/
To summarize:
He had an Essay table as follows:
from django.contrib.auth.models import User
class Essay(models.Model):
title = models.CharField(max_length=666)
body = models.TextField()
author = models.ForeignKey(User, null=True, blank=True)
where multiuser can create essays, so he created a admin.ModelAdmin class as follows:
from myapplication.essay.models import Essay
from django.contrib import admin
class EssayAdmin(admin.ModelAdmin):
list_display = ('title', 'author')
fieldsets = [
(None, { 'fields': [('title','body')] } ),
]
def save_model(self, request, obj, form, change):
if getattr(obj, 'author', None) is None:
obj.author = request.user
obj.save()
Let's say that user B saves a record created by user A. By using this approach above the record will be saved with user B. In some scenarios this might not be the best choice, because each user who saves that record will be "stealing" it. There's a workaround to this, that will save the user only once (the one who creates it):
models.py
from django.contrib.auth.models import User
class Car(models.Model):
created_by = models.ForeignKey(User,editable=False,null=True,blank=True)
car_name = models.CharField(max_length=40)
admin.py
from . models import *
class CarAdmin(admin.ModelAdmin):
list_display = ('car_name','created_by')
actions = None
def save_model(self, request, obj, form, change):
if not obj.created_by:
obj.created_by = request.user
obj.save()
If you don't want to keep foreignkey in you model to user, then in your admin.py override save method
obj.author = request.user.username
obj.save()
This will store the username who is logged in your db.
It's time for a better solution override the get_form method
let's say we have this model
models.py
from django.db import models
from django.contrib.auth.models import User
class Post(models.Model):
title = models.CharField(max_length=256)
content = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
admin.py
class PostAdmin(admin.ModelAdmin):
# you should prevent author field to be manipulated
readonly_fields = ['author']
def get_form(self, request, obj=None, **kwargs):
# here insert/fill the current user name or id from request
Post.author = request.user
return super().get_form(request, obj, **kwargs)
def save_model(self, request, obj, form, change):
obj.author = request.user
obj.last_modified_by = request.user
obj.save()
admin.site.register(Post, PostAdmin)
As per http://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.prepopulated_fields you can't use ForeignKey with the prepopulated_field admin directive, alas
But this thread might help you. In my answer I also link to a Google-scanned version of Pro Django, which has a great solution for this kind of thing. Ideally, am sure it's better if you can buy the book, but Google seems to have most of the relevant chapter anyway.
You can't do it directly. However you can achieve this creating middleware and using current user as global variable. But there is a package already doing it : django-currentuser
First install it then
setting.py
MIDDLEWARE = (
...,
'django_currentuser.middleware.ThreadLocalUserMiddleware',
)
and import it in the model file
from django_currentuser.middleware import ( get_current_user, get_current_authenticated_user)
And use;
class Foo(models.Model):
created_by = CurrentUserField()
updated_by = CurrentUserField(on_update=True)

Categories