One model object per user Django - python

I don't want to extend my userprofile.
I made a new model with name, contact and email.
The problem :
With createview the user is able to create multiple instances of the user_info model.
Is there any chance we can limit user to make only one user_info and update the same everytime.
models.py
class user_info(models.Model):
booked_by = models.CharField(max_length=100)
Name = models.CharField(max_length=40)
contact = models.IntegerField()
email = models.EmailField()
views.py
class user_info_create(LoginRequiredMixin,CreateView):
login_url = 'Mel:user_login'
form_class = user_infoform
template_name = 'Mel/user_info_form.html'
def form_valid(self, form):
form.instance.booked_by = self.request.user
return super(user_info_create, self).form_valid(form)
class user_info_detail(LoginRequiredMixin,DetailView):
login_url = 'Mel:user_login'
model = user_info
context_object_name = "book"
def get_queryset(self):
return user_info.objects.filter(booked_by=self.request.user)

As mentioned by #vorujack You need to create a OneToOne relationship between user_info and your user model. The R and RDBMS stands for Relations. So you need to build relationships between models. At the moment your system doesn't have any relation between user and the profile. However, the correct syntax is
booked_by = models.OneToOneField(User)
Then you need to do
python manage.py makemigrations
python manage.py migrate
if you already have duplicate entries in the table, the second step will fail. In that case you need to clear out the duplicates and run it again. If you have invalid entries in that table, the migration will still fail. So if you don't have any critical data, you might in fact want to clear out the whole table before you do this.
Another point worth noting.
https://legacy.python.org/dev/peps/pep-0008/#id39
Class Names
Class names should normally use the CapWords convention.
The naming convention for functions may be used instead in cases where the >interface is documented and used primarily as a callable.
Note that there is a separate convention for builtin names: most builtin names are single words (or two words run together), with the CapWords convention used only for exception names and builtin constants.
So your classes should really be UserInfo and UserInfoCreate

you can make a relation to your user model like this:
class user_info(models.Model):
booked_by = models.OneToOneField(User)
Name = models.CharField(max_length=40)
contact = models.IntegerField()
email = models.EmailField()
on create view set booked_by field with current user. with this change if user want to create multiple user_info it raised exception and no user_info inserted

Thank you vorujack and e4c5.
Just by doing OneToOneField is not solving the entire problem.
The below code really solved my problem.
If its not correct or can be done in a better way please let me know.
model.py
booked_by = models.OneToOneField(User)
views.py
class user_RedirectView(LoginRequiredMixin,RedirectView):
def get_redirect_url(self):
if user_info.objects.filter(booked_by=self.request.user).exists():
return reverse('Mel:user_update')
else:
return reverse('Mel:user_info_create')

Related

How to check if user is in a certain table

I have my user table in django, and to differ all the users I created two tables, (Teacher and Student).
Both tables are getting an fk from user
So, in order to make authorization how do I check if one's user is in a certain table.
I need to check it this way
def test_func(self):
return self.request.user.check..if..it..exists..in..table
My models are like this.
class Teacher(models.Model):
User = models.OneToOneField(settings.AUTH_USER_MODEL)
This depends on how your models are set up.
If your Teacher model looks something like this;
class Teacher(models.Model):
user = models.ForeignKey(User)
Then you should be able to check if the user is a teacher by using the implicit backref;
self.request.user.teacher_set.exists()
As the question has been updated to show that the model is slightly different than I anticipated, here is an update.
class Teacher(models.Model):
user = models.OneToOneField(User)
Which means that the backref will be a little different.
hasattr(self.request.user, "teacher")
As you've mentioned that you are doing this inside a django template, I'm pretty sure that the following will work:
{% if user.teacher %}
Since you haven't posted your models, I am giving you a rough idea how to do it.
in your views.py -
from .models import Teacher,Student
def test_func(request):
user = request.user
if (Teacher.objects.filter(user=user).count() > 0) or (Student.objects.filter(user=user).count > 0):
#do your stuffs here..
One way is to query both tables:
teacher = Teacher.objects.filter(user=self.request.user)
student = Student.objects.filter(user=self.request.user)
if teacher or student:
# do what you want.
If you put in your relation the argument "related_name" you can do it using inverse relationship
class SomeTable(models.Model):
user = models.ForeignKey(
User, #Your user model or Django one
verbose_name = "User",
related_name = "inverse_relation_name"
)
Then you have to call using keyword arguments for the filters:
SomeTable.inverse_relation_name.filter(id=self.request.user.id) #You will get a queryset
Or
SomeTable.inverse_relation_name.get(id=self.request.user.id) # You will get the object or a exception

Can't disable ForeignKey referential integrity check in Django 1.9

I have a model with two entities, Person and Code. Person is referenced by Code twice, a Person can be either the user of the code or the approver.
What I want to achieve is the following:
if the user provides an existing Person.cusman, no further action is needed.
if the user provides an unknown Person.cusman, a helper code looks up other attributes of the Person (from an external database), and creates a new Person entity.
I have implemented a function triggered by pre_save signal, which creates the missing Person on the fly. It works fine as long as I use python manage.py shell to create a Code with nonexistent Person.
However, when I try to add a new Code using the admin form or a CreateView descendant I always get the following validation error on the HTML form:
Select a valid choice. That choice is not one of the available choices.
Obviously there's a validation happening between clicking on the Save button and the Code.save() method, but I can't figure out which is it. Can you help me which method should I override to accept invalid foreign keys until pre_save creates the referenced entity?
models.py
class Person(models.Model):
cusman = models.CharField(
max_length=10,
primary_key=True)
name = models.CharField(max_length=30)
email = models.EmailField()
def __unicode__(self):
return u'{0} ({1})'.format(self.name, self.cusman)
class Code(models.Model):
user = models.ForeignKey(
Person,
on_delete=models.PROTECT,
db_constraint=False)
approver = models.ForeignKey(
Person,
on_delete=models.PROTECT,
related_name='approves',
db_constraint=False)
signals.py
#receiver(pre_save, sender=Code)
def create_referenced_person(sender, instance, **kwargs):
def create_person_if_doesnt_exist(cusman):
try:
Person = Person.objects.get(pk=cusman)
except Person.DoesNotExist:
Person = Person()
cr = CusmanResolver()
Person_details = cr.get_person_details(cusman)
Person.cusman = Person_details['cusman']
Person.name = Person_details['name']
Person.email = Person_details['email']
Person.save()
create_Person_if_doesnt_exist(instance.user_id)
create_Person_if_doesnt_exist(instance.approver_id)
views.py
class CodeAddForm(ModelForm):
class Meta:
model = Code
fields = [
'user',
'approver',
]
widgets = {
'user': TextInput,
'approver': TextInput
}
class CodeAddView(generic.CreateView):
template_name = 'teladm/code_add.html'
form_class = CodeAddForm
You misunderstood one thing: You shouldn't use TextField to populate ForeignKey, because django foreign keys are populated using dropdown/radio button to refer to the id of the object in another model. The error you got means you provided wrong information that doesn't match any id in another model(Person in your case).
What you can do is: not using ModelForm but Form. You might have some extra work to do after you call form.is_valid(), but at least you could code up your logic however you want.

what does exclude in the meta class of django mean?

I came across this code:
drinker/models.py:
from django.db import models
from django.db.models.signals import post_save
from django.contrib.auth.models import User
class Drinker(models.Model):
user = models.OneToOneField(User)
birthday = models.DateField()
name = models.CharField(max_length=100)
def __unicode__(self):
return self.name
drinker/forms.py:
from django import forms
from django.contrib.auth.models import User
from django.forms import ModelForm
from drinker.models import Drinker
class RegistrationForm(ModelForm):
username = forms.CharField(label=(u'User Name'))
email = forms.EmailField(label=(u'Email Address'))
password = forms.CharField(label=(u'Password'), widget=forms.PasswordInput(render_value=False))
password1 = forms.CharField(label=(u'Verify Password'), widget=forms.PasswordInput(render_value=False))
class Meta:
model = Drinker
exclude = ('user',)
def clean_username(self):
username = self.cleaned_data['username']
try:
User.objects.get(username=username)
except User.DoesNotExist:
return username
raise forms.ValidationError("That username is already taken, please select another.")
def clean(self):
if self.cleaned_data['password'] != self.cleaned_data['password1']:
raise forms.ValidationError("The passwords did not match. Please try again.")
return self.cleaned_data
My Question is about the inner class meta which as two attributes:
model=Drinker
exclude=('user`,)
I have a not-so-clear understanding of how this meta class work. I have read the documentation but I am still confused. Can you kindly explain what those two lines mean and what their purpose is?
Thanks
The exclude attribute tells Django what fields from the model not to include in the form.
Quoting the Selecting fields to use section of the model form documentation:
2. Set the exclude attribute of the ModelForm’s inner Meta class to a list of fields to be excluded from the form.
The model line simply tells Django what model to take the fields from; together the two lines tell Django to give RegistrationForm fields based on all fields on the Drinker model, except 'user'. For the given Drinker model, that's birthday and name.
These fields are added to the other form fields already defined on the form. If the Drinker model gained more fields, those would automatically be part of the form too.
See the Overriding the default fields section of the same chapter:
When you explicitly instantiate a form field like this, it is important to understand how ModelForm and regular Form are related.
ModelForm is a regular Form which can automatically generate certain fields. The fields that are automatically generated depend on the content of the Meta class and on which fields have already been defined declaratively. Basically, ModelForm will only generate fields that are missing from the form, or in other words, fields that weren’t defined declaratively.
The inner Meta class is just a convenient way to create a namespace for such configuration on your form class for the Django framework to find. All Django now has to do is introspect Form.Meta and see what attributes are defined there.
Note that using exclude can lead to security problems. From the same documenation:
It is strongly recommended that you explicitly set all fields that should be edited in the form using the fields attribute. Failure to do so can easily lead to security problems when a form unexpectedly allows a user to set certain fields, especially when new fields are added to a model. Depending on how the form is rendered, the problem may not even be visible on the web page.
The alternative approach would be to include all fields automatically, or blacklist only some. This fundamental approach is known to be much less secure and has led to serious exploits on major websites (e.g. GitHub).
fields = exclude() and fields = '__all__' - means display all the fields
exclude = ('password',) - means exclude password field
fields = ('user','email',) - means display only email field and userfield
in short : fields you want to show up in the form should be mentioned in 'fields' attribute ex:
fields = '__all__' #will show all the fields from the model in the form
'exclude' does the opposite
exclude = ['title'] # don't show the title field

Django: combine two ForeignKeys into one field

I need to implement the following:
The user shall be presented with a form that will have a drop down choice menu consisting of property names. There are two types of properties: general properties, i.e. properties common for all users and custom properties, i.e. properties that each user has defined prior to that. The models would look something like that:
class GeneralPropertyName(models.Model):
name = models.CharField(max_length=20)
class CustomPropertyName(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=20)
The drop down menu should have all general properties and only those custom properties that pertain to the user.
First question: how to define such a model?
I need to: 1. somehow unify both properties, 2. take only those items from CustomPropertyName that pertain to the user
class SpecData(models.Model):
user = models.ForeignKey(User)
selection_title = models.CharField(max_length=20)
property = ForeignKey(GeneralPropertyName) ??UNIFY??? ForeignKey(CustomPropertyName)
Second, is there anything special that needs to be done with ModelForm?
class SpecDataForm(ModelForm):
class Meta:
model = SpecData
And the 3rd question is what needs to be done in the view? I will need to use inline formsets since I will have a few dynamic forms like that.
def index(request):
user = User.objects.get(username=request.user.username)
specdataFormSet = inlineformset_factory(User, SpecData, form=SpecDataForm, extra=30)
...
specdata_formset = specdataFormSet(instance=user, prefix='specdata_set')
...
Thanks.
EDIT: Adjusted juliocesar's suggestion to include formsets. Somehow I am getting the following error message: Cannot resolve keyword 'property' into field. Choices are: id, name, selection_title, user
def index(request):
user = User.objects.get(username=request.user.username)
user_specdata_form = UserSpecDataForm(user=user)
SpecdataFormSet = inlineformset_factory(User, SpecData, form=user_specdata_form, extra=30)
You can use a GenericForeignKey to handle it, but you still need more to solve your further questions about forms and view.
I have made an example of how you solve your problem (logged user can select from General properties and his Custom properties, non-logged user only can select General properties). I used model inheritance for the properties (In your sample code it seems that a CustomPropertyName is a PropertyName with other fields). I think inheritance is an easier and a more basic concept than ContentTypes and it fits to your needs.
NOTE: I remove some code like imports to simplify the code.
1) models.py file:
class PropertyName(models.Model):
name = models.CharField(max_length=20)
def __unicode__(self):
return self.name
class CustomPropertyName(PropertyName): # <-- Inheritance!!
user = models.ForeignKey(User)
def __unicode__(self):
return self.name
class SpecData(models.Model):
user = models.ForeignKey(User)
selection_title = models.CharField(max_length=20)
property = models.ForeignKey(PropertyName)
NOTES: The field SpecData.property points to PropertyName since all properties are saved in the PropertyName's database table.
2) forms.py file:
from django import forms
from django.db.models import Q
from models import SpecData, PropertyName
def UserSpecDataForm(user=None):
UserPropertiesQueryset = PropertyName.objects.filter(Q(custompropertyname__user=None) | Q(custompropertyname__user__id=user.id))
class SpecDataForm(forms.ModelForm):
property = forms.ModelChoiceField(queryset=UserPropertiesQueryset)
class Meta:
model = SpecData
exclude = ('user',)
return SpecDataForm
NOTES: The trick here is to generate the form SpecDataForm dynamically, by filtering properties according the user specified in the parameter.
3) views.py file:
from forms import UserSpecDataForm
def index(request):
if request.POST:
form = UserSpecDataForm(request.user)(request.POST) # instance=user
if form.is_valid():
spec_data = form.save(commit=False)
spec_data.user = request.user
spec_data.save()
else:
form = UserSpecDataForm(request.user)()
return render_to_response('properties.html', {'form': form}, context_instance=RequestContext(request))
NOTES: Nothing special here, just a call to form.UserSpecDataForm(request.user) that returns the form class and then instantiate. Also setted the logged-in user to the object returned on save since It was excluded in the form to not show in front-end.
Following this basic example you can do the same with formsets if you need it.
UPDATE:
Formset can be used by adding following code to the view:
user_specdata_form = UserSpecDataForm(user=request.user)
SpecdataFormSet = inlineformset_factory(User, SpecData, form=user_specdata_form, extra=30)
The complete project sample can be downloaded from http://ge.tt/904Wg7O1/v/0
Hope this helps
1a) have you looked into django's ContentType framework this will allow you to have generic foreign keys and you can put restrictions on what types of models are acceptable to store in.
1b) I think that the validation for accepting what type of foreign key is acceptable shouldn't be in your model but should be part of your form validation before saving.
2) If you do use a model form you're going to have to define your own custom widget for the propery field. This means you're probably going to have to write you're own render function to render the html from the field. You should also define your own validation function on the form to make sure that only the appropriate data is acceptable to save.
3) I don't think you'll have to do anything you aren't already doing in the views
Use GenericForeignKey:
class SpecData(models.Model):
user = models.ForeignKey(User)
selection_title = models.CharField(max_length=20)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
property = GenericForeignKey('content_type', 'object_id')
You can use this to combine the two fields(type & id) into a single choice field.
One way is that you have only one model, make user nullable:
class PropertyName(models.Model):
user = models.ForeignKey(User, null=True, blank=True)
name = models.CharField(max_length=20)
class SpecData(models.Model):
user = models.ForeignKey(User)
selection_title = models.CharField(max_length=20)
property = ForeignKey(PropertyName)
So, if user is not set, it is a general property. If it is set, it is related to this user.
However, please note that if you need unique property names, that NULL != NULL.
Of course, the suggested GenericForeignKey solution is better for some cases.
Also, you can easily make the normal (non-model) form with that you describe and separate form logic from model logic.

Django link to existing item or create if unique in multi-model form

I have a form which uses multiple models, which have relationships. If the data for one of the forms matches an existing entry, I want to have the foreign key map to the existing entry rather than create a duplicate entry. However, is_valid() fails for the already existing entry and I'm not sure if it is safe to use the form data before it has been cleaned to do a database lookup for existence first. Perhaps I need to change the clean function to ignore the uniqueness requirement and then handle it in the view?
Here's an example, a user enters their name, city and state in a web form. If that city is already known to the database, then the foreign key for the person should just point to the existing entry. If it is a new city, it should be added to the database. So it is always a CREATE for Person, but it may or may not be a CREATE for Hometown.
models.py
class Person(models.Model):
name = models.CharField()
hometown = models.ForeignKey('Hometown')
class Hometown(models.Model):
cityName = models.CharField()
stateName = models.CharField()
mascot = models.CharField()
#If same city and state, it's the same place
class Meta:
unique_together = ("cityName", "stateName")
forms.py
class PersonForm(ModelForm):
class Meta:
model = Person
exclude = ('hometown')
class HometownForm(ModelForm):
class Meta:
model = Hometown
views.py
def newPerson(request):
if request.method == 'POST':
person = PersonForm(request.POST)
hometown = HometownForm(request.POST)
if (person.is_valid() and hometown.is_valid():
p = person.save(commit=False)
h = Hometown.objects.get_or_create(**hometown.cleaned_data)
p.hometown = h
p.save()
This code doesn't work, because hometown.is_valid() will be False if that city/state pair is already in the database. Should I override the clean() function to allow ignore the uniqueness requirement (enforcing it in the view by using get_or_create) or is that an indication that my design is fundamentally the wrong way of addressing this problem?
A similar question on stackoverflow contained the information I needed. Free-form input for ForeignKey Field on a Django ModelForm
As applied to my original question, I removed the HometownForm and instead added the relevant fields to PersonForm, then made the save() function of PersonForm handle the get_or_create() behavior for the Hometown.

Categories

Resources