Using admin site in Django - django

I have a model with two date fields, for submitted and published and a boolean field for approved.
class BlogModel(models.Model):
title = models.CharField(max_length=100)
article = models.CharField(max_length=255)
pub_date = models.DateTimeField('date published')
submitted_date = models.DateTimeField('date submitted')
author = models.CharField(max_length=255)
approved = models.BooleanField(default=False)
def __str__(self): # __unicode__ on Python 2
return 'approved, ' + str(self.approved) + ' article, ' + self.article
I have included this model in admin.py. Admin can approve the article, but is it possible to have the pub_date be the current time when the article is approved in admin?
UPDATE These models work for me in forms.py as suggested by Raja Simon
class BlogForm(forms.ModelForm):
class Meta:
model = BlogModel
fields = '__all__'
# fields = ('id', 'title', 'article')
def save(self, force_insert=False, force_update=False, commit=True):
m = super(BlogForm, self).save(commit=False)
# do custom stuff
m.pub_date = timezone.now()
if commit:
m.save()
return m
and admin.py
class BlogModelAdmin(admin.ModelAdmin):
form = BlogForm
fields = ('title', 'article', 'pub_date', 'submitted_date', 'author', 'approved')
# fields = '__all__' won't work here, each field needs to be added individually as above
pass
admin.site.register(BlogModel, BlogModelAdmin)
Thanks

You can override form save in admin
class BlogModelAdmin(admin.ModelAdmin):
form = BlogModelForm
pass
admin.site.register(BlogModel, BlogModelAdmin)
And in forms
class BlogModelForm(ModelForm):
class Meta:
model = BlogModel
fields = '__all__'
def save(self, force_insert=False, force_update=False, commit=True):
m = super(BlogModelForm, self).save(commit=False)
# do custom stuff
m.pub_date = timezone.now()
if commit:
m.save()
return m

Possible by over riding the save_model method in your admin
def save_model(self, request, obj, form, change):
if obj.approved:
obj.pub_date = timezone.now()
This is probably superior to overriding the save method in the model itself.

Related

Django - Filtering Post Form option by currently logged-in User's Vehicle

I'm a django newbie and i'm making a form where a User can make a Post and pick one of his Vehicles for the Post. The Vehicle and the Post models are created like so:
*blog/models.py*
class Post(models.Model):
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
vehicle = models.ForeignKey(Vehicle, on_delete=models.CASCADE, null=True)
def get_absolute_url(self):
return reverse('post-detail', kwargs ={'pk': self.pk} )
*vehicles/models.py*
class Vehicle(models.Model)*:
TESLA = 'TESLA'
MAZDA = 'MAZDA'
VOLVO = 'VOLVO'
VEHICLE_CHOICES = (
(TESLA, "Tesla"),
(MAZDA, "Mazda"),
(VOLVO, "Volvo"),
)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
model = models.CharField(max_length=9,
choices=VEHICLE_CHOICES,
default=TESLA)
def __str__(self):
return self.model
My blog views:
*blog/views.py*
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = [ 'vehicle']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
I would like to filter the vehicles so that only the current logged in User's vehicles
show up in the form, i've tried a variety of different solutions but I seem to be going around in circles, if you could help me out that would be awesome. Thanks!
Since you are using createview, you can create a form in forms.py. First you have to send the logged in user to the form, then in the form, pop the user from kwargs and use it to filter the vehicles.
views.py
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
form_class = PostForm
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['vehicle']
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super().__init__(*args, **kwargs)
self.fields['vehicle'].queryset = Vehicle.objects.filter(owner=user)

Is there a more elegant way to implement an modification to save() method when using CreateView and ModelForm in Django?

I'm learning the Class-Based View and ModelForm of Django, and I feel so confused with those things.
I want to create a page where users can post articles.
My implementation is as following:
models.py
class Post(models.Model):
id = models.CharField(primary_key=True, null=False, max_length=20)
owner = models.ForeignKey(User, on_delete=models.CASCADE, null=False)
content = models.TextField()
count_like = models.IntegerField(default=0)
created_time = models.DateTimeField()
last_modified = models.DateTimeField()
def save(self, *args, **kwargs):
''' On save, update last_modified '''
if not self.id:
count = Post.objects.count()
self.id = "PO" + str(count)
self.created_time = timezone.now()
self.last_modified = timezone.now()
return super(Post, self).save(*args, **kwargs)
def get_absolute_url(self):
print("pk"*100, self.pk)
return reverse('post_detail', kwargs={'pk': self.pk})
forms.py
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['content']
def save(self):
return super().save(commit=False)
views.py
class PostCreateView(generic.CreateView):
model = Post
form_class = PostForm
template_name="post/create.jinja"
def form_valid(self, form):
self.object = form.save()
self.object.owner = self.request.user
self.object.save()
return HttpResponseRedirect(self.get_success_url())
urlpatterns
path('create/', PostCreateView.as_view(), name="post_create")
I checked and this works.
As you can see, the Post model has many attributes, but I just want users to fill 1 field content, the others would be automatically initiated. Is there any way to improve my implementation, because it's seperated into many places (model save() method, form save() method, valid_form() method).
One more question is what is self.object role? After assigned to a Post model instance, what would it be used for?
Please help me, if you don't understand what I say please ask in comment. Thanks ^^

Django Rest Framework: How to associate the object with the user when posting the object

I'm new to creating REST API so I might misunderstand something.
I'm creating REST API using Django Rest Framework. And I'm trying to create an object and send it from my mobile app.
However, API returns 400. I think it still cannot associate the object with the request user and I'm wondering how to do it.
models.py
class Item(models.Model):
item_name = models.CharField()
created_at = models.DateTimeField(auto_now_add=True)
created_by = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
serializers.py
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Item
fields = ('item_name', 'created_by')
and views.py
class ListItems(generics.ListCreateAPIView):
queryset = Item.objects.all()
serializer_class = ItemSerializer
What I want to know is how to associate the object with the request user when posting the object like as we do like
if form.is_valid():
item = form.save(commit=False)
item.created_by = request.user
item.save()
I think the easiest approach is like this:
class ItemSerializer(serializers.ModelSerializer):
created_by = serializers.HiddenField(
default=serializers.CurrentUserDefault()
)
Reference can be found here
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Item
fields = ('item_name',)
class ListItems(generics.ListCreateAPIView):
...
def perform_create(self, serializer):
serializer.save(created_by=self.request.user)
you can do this way
One of the possible way to overwrite serializer_create method. As user is not associated with request.data first we need to make sure, this is write_only field and also need to assign current user from modelSerializer's self.context.request.user. Following addition should solve the problem.
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Item
fields = ('item_name', 'created_by')
extra_kwargs = {'created_by': {'write_only': True}}
def create(self, validated_data):
item = Item(
item_name=validated_data['item_name'],
created_by=self.context.request.user
)
item.save()
return item
Reference link
It works for me
models.py
class Category(models.Model):
name = models.CharField('Category', max_length=200, unique=True, help_text='Name of the category')
slug = models.SlugField('Slug', max_length=100, db_index=True, unique=True, help_text='Name of the category in format URL')
def __str__(self):
return (self.name)
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(Category, self).save(*args, **kwargs)
serializers.py
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = [
'id', 'name', 'slug'
]
read_only_fields = [
'slug',
]
Finally, I get the user in the view, before to save the post.
views.py
class CategoryList(APIView):te a new category instance.
permission_classes = (IsAuthenticatedOrReadOnly,)
def get(self, request, format=None):
categories = Category.objects.all()
serializer = CategorySerializer(categories, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
def post(self, request, format=None):
serializer = CategorySerializer(data=request.data)
if serializer.is_valid():
serializer.save(created_by=self.request.user)
Response(serializer.data, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

get current user for 'created_by' field in model class

I'm currently working on a Django app, and I'm trying to set the current user as default on a model, but it doesn't work.
created_by = models.ForeignKey(User, default=request.user, null=True, blank=True, on_delete=models.DO_NOTHING, related_name='created_by')
I tried to override the save() method but it doesn't work either, anyone has any experience on this matter ?
Thanks a lot in advance for your help
Refer official doc. It explained it pretty well. An example is also there
from django.views.generic.edit import CreateView
from myapp.models import Author
class AuthorCreate(CreateView):
model = Author
fields = ['name']
def form_valid(self, form):
form.instance.created_by = self.request.user
return super().form_valid(form)
https://docs.djangoproject.com/en/2.1/topics/class-based-views/generic-editing/#models-and-request-user
If anyone encounters this problem I followed the advice of a_k_v and did it in views.
Here is how I did it :
I added two fields in my class :
class Class(models.Model):
created_by = models.ForeignKey(User, on_delete=models.DO_NOTHING, blank=True, null=True, related_name='create')
updated_by = models.ForeignKey(User, on_delete=models.DO_NOTHING, blank=True, null=True, related_name='update')
then created a function :
def created_updated(model, request):
obj = model.objects.latest('pk')
if obj.created_by is None:
obj.created_by = request.user
obj.updated_by = request.user
obj.save()
to get it into my views.py :
if request.method == 'POST':
form = AddVacation(request.POST)
if form.is_valid:
form.save()
created_updated(Vacation, request)
If your model is like below model.py
class Post(models.Model):
title = models.CharField(max_length=500)
created_by = models.ForeignKey(User, on_delete=models.SET_NULL, null= True)
In model admin class in admin.py add the following method
class PostAdmin(admin.ModelAdmin):
readonly_fields = ('created_by',)
list_display = ('title', 'created_by')
def save_model(self, request, obj, form, change):
if obj.id == None:
obj.created_by = request.user
super().save_model(request, obj, form, change)
else:
super().save_model(request, obj, form, change)
admin.site.register(Post, PostAdmin)

Extend UserCreationForm for extended User in Django

I extended my django user and need to create a registration form now.
I got most of it figured out but I don't know how to exclude fields I don't need during registration. Right know I see all fields in the registration form.
Here is the code:
models.py
class Artist(Model):
user = OneToOneField(User, unique=True)
address = CharField(max_length=50)
city = CharField(max_length=30)
ustid = CharField(max_length=14)
date_of_birth = DateField()
bio = CharField(max_length=500)
def __unicode__(self):
return self.user.get_full_name()
User.profile = property(lambda u: Artist.objects.get_or_create(user=u)[0])
forms.py
class RegistrationForm(UserCreationForm):
class Meta:
model = User
def __init__(self, *args, **kwargs):
super(RegistrationForm, self).__init__(*args, **kwargs)
artist_kwargs = kwargs.copy()
if kwargs.has_key('instance'):
self.artist = kwargs['instance'].artist
artist_kwargs['instance'] = self.artist
self.artist_form = ArtistForm(*args, **artist_kwargs)
self.fields.update(self.artist_form.fields)
self.initial.update(self.artist_form.initial)
def clean(self):
cleaned_data = super(RegistrationForm, self).clean()
self.errors.update(self.artist_form.errors)
return cleaned_data
def save(self, commit=True):
self.artist_form.save(commit)
return super(RegistrationForm, self).save(commit)
How do I exclude fields?
class Meta:
model = User
exclude = ('bio',)
You can't include or exclude fields that are not a member of the meta model.
What you can do is doing that in each form. In this case the UserCreationForm is extended by the ArtistForm. Just restrict the fields in the form that belong to the right meta model.