Limit choices to - python

I have a model named Project which has a m2m field users. I have a task model with a FK project. And it has a field assigned_to. How can i limit the choices of assigned_to to only the users of the current project?

You could do this another way, using this nifty form factory trick.
def make_task_form(project):
class _TaskForm(forms.Form):
assigned_to = forms.ModelChoiceField(
queryset=User.objects.filter(user__project=project))
class Meta:
model = Task
return _TaskForm
Then from your view code you can do something like this:
project = Project.objects.get(id=234)
form_class = make_task_form(project)
...
form = form_class(request.POST)

You need to create a custom form for the admin.
Your form should contain a ModelChoiceField in which you can specify a queryset parameter that defines what the available choices are. This form can be a ModelForm.
(the following example assumes users have an FK to your Project model)
forms.py
from django import forms
class TaskForm(forms.ModelForm):
assigned_to = forms.ModelChoiceField(queryset=Users.objects.filter(user__project=project))
class Meta:
model = Task
Then assign the form to the ModelAdmin.
admin.py
from django.contrib import admin
from models import Task
from forms import TaskForm
class TaskAdmin(admin.ModelAdmin):
form = TaskForm
admin.site.register(Task, TaskAdmin)

Related

Django RF, field level validation to check if requesting user is Admin

My Django Rest Framework project have models field where any authenticated users can create model instance.
However I wanted to make sure that only Django Admin can change the accepted field value.
What is the best way to prevent other users from changing the accepted field ?
Pls note that I want to keep permission for authenticated users to create model instance keeping default accepted field.
MODELS.PY
class PO(models.Model):
name = models.CharField(max_length=100)
accepted=models.BooleanField(default=False) # I want this field to be changed only by admin user
VIEWS.PY
class POcreate(generics.CreateAPIView):
queryset = PO.objects.all()
serializer_class = POserializer
permission_classes = [permissions.IsAuthenticated]
SERIALIZER.PY
class POserializer(serializers.ModelSerializer):
class Meta:
model=PO
fields='__all__'
changed view layer and serializer
def get_serializer_class(self):
if self.request.user.is_staff:
return POAdminserializer
return POserializer
class POAdminserializer(serializers.ModelSerializer):
class Meta:
model=PO
fields='__all__'
class POserializer(serializers.ModelSerializer):
class Meta:
model=PO
exclude=('created','accepted','delivered','rejected','rejected_reason')

Archive section for objects in django admin

I want to customize Django Admin to have specific section for objects of my models (Such as Post or Product models) that use as an archive section.
I now that, I need one field in my models that shown status of objects (Such as is_archive field), but I don't have any idea about how to display them in Django Admin.
Does anyone have an opinion on this?
Create Proxy model for model you need
Create separate section in your admin panel for this proxy model
Override get_queryset() for it.
models.py
from django.db import models
class Post(models.Model):
...
is_archive = models.BooleanField(default = False)
...
class PostProxy(Post):
class Meta:
proxy = True
admin.py
from django.contrib import admin
from .models import *
#admin.register(Post)
class PostAdmin(admin.ModelAdmin):
...
#admin.register(PostProxy)
class PostProxyAdmin(admin.ModelAdmin):
...
def get_queryset(self, request):
return super().get_queryset(request).filter(is_archive=True)

is it necessary to implement ModelForm in our project to implement a CreateView (CBV) in Django 2.0.2?

I am a beginner programming in Django framework and I am learning how to implement a CreateView (a class based view for creating a form based on a model) in my views.py file.
No, you don't the view will automatically create a model form for you, but you have to option of overwriting it.
Let's assume you have MyModel, you can do this:
from myapp.models import MyModel
# views.py
class MyCreateView(CreateView):
model = MyModel
fields = ['something', 'somethingelse'] # these are fields from MyModel
If you do not specify the fields Django will throw an error.
If you want to customize your form validation in some way, you can do this:
# forms.py
class MyForm(ModelForm):
class Meta:
model = MyModel
fields = ['something'] # form fields that map to the model
# ... do some custom stuff
# views.py
class MyCreateView(CreateView):
model = MyModel
form_class = MyForm
Notice that we are not specifying the fields anymore on MyView because if we would it will also throw an error, and the reasons is because the view will fetch the fields from the form.
More information: https://docs.djangoproject.com/en/2.1/topics/class-based-views/generic-editing/
Code that handles the form_class: https://github.com/django/django/blob/master/django/views/generic/edit.py#L74
You don't need to create a ModelForm, you just need to specify the model in the model attribute, e.g. for an Author model set model = Author.
CreateView uses the ModelFormMixin, which uses this model attribute to handle the ModelForm:
from django.views.generic.edit import CreateView
from myapp.models import Author
class AuthorCreate(CreateView):
model = Author
fields = ['name']
See more here: https://docs.djangoproject.com/en/2.1/ref/class-based-views/mixins-editing/#django.views.generic.edit.ModelFormMixin.model

Indirect inline in Django admin

I have the following models:
class UserProfile(models.Model):
user = models.OneToOneField(User)
class Property(models.Model):
user = models.ForeignKey(User)
I would like to create a TabularInline displaying every Property connected to a particular UserProfile on its Django admin page. The problem here is, of course, that Property does not have a ForeignKey directly to UserProfile, so I cannot simply write
class PropertyTabularInline(admin.TabularInline):
model = Property
class UserProfileAdmin(admin.ModelAdmin):
inlines = (PropertyTabularInline,)
How can I easily do what I want?
You can overwrite the User admin page to display both the Profile and the Property models.
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from myapp.models import *
class ProfileInline(admin.TabularInline):
model = Profile
class PropertyInline(admin.TabularInline):
model = Property
class UserAdmin(UserAdmin):
inlines = (ProfileInline, PropertyInline,)
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
You can also remove any unwanted/unused User properties from being displayed (e.g. Groups or Permissions)
more here: https://docs.djangoproject.com/en/1.8/topics/auth/customizing/#extending-the-existing-user-model
and here:
https://docs.djangoproject.com/en/1.8/topics/auth/customizing/#a-full-example
class PropertyTabularInline(admin.TabularInline):
model = Property
def formfield_for_dbfield(self, field, **kwargs):
if field.name == 'user':
# implement your method to get userprofile object from request here.
user_profile = self.get_object(kwargs['request'], UserProfile)
kwargs["queryset"] = Property.objects.filter(user=user_profile)
return super(PropertyInLine, self).formfield_for_dbfield(field, **kwargs)
once this is done, you can add this inline to user UserProfileAdmin like:
class UserProfileAdmin(admin.ModelAdmin):
inlines = (PropertyTabularInline,)
Haven't tested it, but that should work.
It is achievable by making one change in your models.
Instead of creating OneToOne relationship from UserProfile to User, subclass User creating UserProfile. Code should look like that:
class UserProfile(User):
# some other fields, no relation to User model
class Property(models.Model):
user = models.ForeignKey(User)
That will result in creating UserProfile model that have hidden OneToOne relation to User model, it won't duplicate user model.
After doing that change, your code will work. There are some changes under the hood, like UserProfile no longer have it's own ID, you can access fields from User inside UserProfile and it's hard to swap User model using settings.AUTH_USER_MODEL (that will require creating some custom function returning proper type and changing migration by hand) but if this is not a problem for you, it may be good solution.

django model Form. Include fields from related models

I have a model, called Student, which has some fields, and a OneToOne relationship with a user (django.contrib.auth.User).
class Student(models.Model):
phone = models.CharField(max_length = 25 )
birthdate = models.DateField(null=True)
gender = models.CharField(max_length=1,choices = GENDER_CHOICES)
city = models.CharField(max_length = 50)
personalInfo = models.TextField()
user = models.OneToOneField(User,unique=True)
Then, I have a ModelForm for that model
class StudentForm (forms.ModelForm):
class Meta:
model = Student
Using the fields attribute in class Meta, I've managed to show only some fields in a template. However, can I indicate which user fields to show?
Something as:
fields =('personalInfo','user.username')
is currently not showing anything. Works with only StudentFields though/
Thanks in advance.
A common practice is to use 2 forms to achieve your goal.
A form for the User Model:
class UserForm(forms.ModelForm):
... Do stuff if necessary ...
class Meta:
model = User
fields = ('the_fields', 'you_want')
A form for the Student Model:
class StudentForm (forms.ModelForm):
... Do other stuff if necessary ...
class Meta:
model = Student
fields = ('the_fields', 'you_want')
Use both those forms in your view (example of usage):
def register(request):
if request.method == 'POST':
user_form = UserForm(request.POST)
student_form = StudentForm(request.POST)
if user_form.is_valid() and student_form.is_valid():
user_form.save()
student_form.save()
Render the forms together in your template:
<form action="." method="post">
{% csrf_token %}
{{ user_form.as_p }}
{{ student_form.as_p }}
<input type="submit" value="Submit">
</form>
Another option would be for you to change the relationship from OneToOne to ForeignKey (this completely depends on you and I just mention it, not recommend it) and use the inline_formsets to achieve the desired outcome.
Both answers are correct: Inline Formsets make doing this easy.
Be aware, however, that the inline can only go one way: from the model that has the foreign key in it. Without having primary keys in both (bad, since you could then have A -> B and then B -> A2), you cannot have the inline formset in the related_to model.
For instance, if you have a UserProfile class, and want to be able to have these, when shown, have the User object that is related shown as in inline, you will be out of luck.
You can have custom fields on a ModelForm, and use this as a more flexible way, but be aware that it is no longer 'automatic' like a standard ModelForm/inline formset.
An alternative method that you could consider is to create a custom user model by extending the AbstractUser or AbstractBaseUser models rather than using a one-to-one link with a Profile model (in this case the Student model). This would create a single extended User model that you can use to create a single ModelForm.
For instance one way to do this would be to extend the AbstractUser model:
from django.contrib.auth.models import AbstractUser
class Student(AbstractUser):
phone = models.CharField(max_length = 25 )
birthdate = models.DateField(null=True)
gender = models.CharField(max_length=1,choices = GENDER_CHOICES)
city = models.CharField(max_length = 50)
personalInfo = models.TextField()
# user = models.OneToOneField(User,unique=True) <= no longer required
In settings.py file, update the AUTH_USER_MODEL
AUTH_USER_MODEL = 'appname.models.Student'
update the model in your Admin:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import Student
admin.site.register(Student, UserAdmin)
Then you can use a single ModelForm that has both the additional fields you require as well as the fields in the original User model. In forms.py
from .models import Student
class StudentForm (forms.ModelForm):
class Meta:
model = Student
fields = ['personalInfo', 'username']
A more complicated way would be to extend the AbstractBaseUser, this is described in detail in the docs.
However, I'm not sure whether creating a custom user model this way in order to have a convenient single ModelForm makes sense for your use case. This is a design decision you have to make since creating custom User models can be a tricky exercise.
From my understanding you want to update the username field of auth.User which is OneToOne relation with Student, this is what I would do...
class StudentForm (forms.ModelForm):
username = forms.Charfield(label=_('Username'))
class Meta:
model = Student
fields = ('personalInfo',)
def clean_username(self):
# using clean method change the value
# you can put your logic here to update auth.User
username = self.cleaned_data('username')
# get AUTH USER MODEL
in_db = get_user_model()._default_manager.update_or_create(username=username)
hope this helps :)

Categories

Resources