Current user as a comment's autor (DJANGO) - django

I have create a model called 'Comentario' where the logged user can create his own comment. How can I do to automatically save as the author of the comment the logged user. Here I show my schema:
models.py
class Comentario (models.Model):
titulo = models.CharField(max_length=50)
texto = models.CharField(max_length=200)
autor = models.ForeignKey (Perfil, editable=False, blank=True)
fecha_publicacion = models.DateTimeField(auto_now_add=True)
tag = models.ManyToManyField(Tags, blank=True)
def __str__(self):
return (self.titulo)
views.py
class ComentarioCreate (LoginRequiredMixin,CreateView):
model = Comentario
form_class = ComentarioForm
template_name = 'home/comentario_form.html'
success_url = reverse_lazy ('home:listar')
def save(self):
autor=self.request.user.username
user.save()
forms.py
class ComentarioForm(forms.ModelForm):
class Meta:
model = Comentario
fields = [
'titulo',
'texto',
'tag',
]
labels = {
'titulo': 'Titulo',
'texto' : 'Descripcion',
'tag' : 'Etiquetas',
}
widgets = {
'titulo':forms.TextInput(attrs={'class':'form-control'}),
'texto':forms.TextInput(attrs={'class':'form-control'}),
'tag':forms.CheckboxSelectMultiple(),
}
Perfil is a model which inherits form AbstractUser.
models.py
class Perfil(AbstractUser):
nom_puesto = models.ForeignKey(Puesto, blank = True)
def __str__(self):
return '{}'.format(self.username)
How can I do to have in the field 'autor' the username of the logged user?
thank you for your answer!

The method to override is form_valid, not save; see models and request.user which explains exactly what to do.

Related

How to filter data dynamically by supply values from form using django

I want to filter Blog Post objects or records based on the Post Category and a User that uploaded the Post record, it gives me an error when I try to do filter, this is the error.
ValueError at /dashboard/filter-post/
The QuerySet value for an exact lookup must be limited to one result using slicing.
Here is my models.py
class Category(models.Model):
cat_name = models.CharField(max_length=100, verbose_name='Category Name')
cat_desc = models.TextField(blank=True, null=True)
def __str__(self):
return self.cat_name
class Meta():
verbose_name_plural='Category'
class Post(models.Model):
pst_title = models.CharField(max_length=150)
pst_image = models.ImageField(blank=True, null=True, upload_to='uploads/')
user = models.ForeignKey(User, on_delete=models.CASCADE)
category = models.ManyToManyField(Category)
content = models.TextField()
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.pst_title
#property
def img_url(self):
if self.pst_image:
return self.pst_image.url
on forms.py
class FilterForm(forms.ModelForm):
user = forms.ModelChoiceField(
queryset=User.objects.all(),
widget=forms.Select(attrs={'class': 'form-control'}))
category = forms.ModelMultipleChoiceField(
queryset=Category.objects.all(),
widget=forms.SelectMultiple(attrs={'class': 'form-control js-example-disabled-results'}))
catch_bot = forms.CharField(required=False,
widget=forms.HiddenInput, validators=[validators.MaxLengthValidator(0)])
class Meta():
fields = ['user', 'category' ]
model = Post
on views.py
def filter_post(request):
post = FilterForm(request.GET)
queryset = Post.objects.all()
if post.is_valid():
user=post.cleaned_data.get('user')
category=post.cleaned_data.get('category')
if user and category:
queryset = queryset.filter(user__username=user, category__cat_name=category)
return render(request, 'backend/filter-post.html', {'query':queryset, 'post':post})
I am having challenges properly filtering this in my views any help?
Try this:
instead of this:
queryset = queryset.filter(user__username=user, category__cat_name=category)
use this:
queryset = queryset.filter(user=user, category=category)
Also don't name your model fields after the model name, just use name instead of pst_name or cat_name, you will see that when you will try access these values there will be no confusion.
UPDATE
Ok, maybe try to rewrite your view like this:
def filter_post(request):
posts = Post.objects.all()
form = FilterForm(request.GET) # its best practice to call your form instance `form` in the view so that the next line has better readability
if form.is_valid():
user=post.cleaned_data['user']
category=post.cleaned_data['category']
if user:
posts = posts.filter(user=user)
if category:
posts = posts.filter(category=category)
return render(request, 'backend/filter-post.html', {'posts':posts})

Getting pk from selected object in a generic ListView html to populate a FK for a new form (CBV)

I'm trying to make a carwash app and I am having issues getting the wash form populated with the vehicle's pk. I've tried with "get_initial", "get_context_data", "form_valid", passing {% url 'appname:urlname' object.pk %} in a html button, even thought about formsets. So if you can guide this noob in polishment (me), I would greeeeaaaatly apreciate it!
so this is what the code is simplified to...
model:
class Car(models.Model):
carplates = models.CharField(max_length=50, unique=True, null=False)
owner = models.ForeignKey(User, on_delete=models.CASCADE, null=False)
class Wash(models.Model):
vehicle_to_wash = models.ForeignKey(Car, on_delete=models.CASCADE)
specific_comment = models.TextField(max_length=500, blank=True, null=True
form:
class WashServiceForm(forms.ModelForm):
class Meta:
model = Wash
fields = ['specific_comment', 'vehicle_to_wash']
views:
class CarList(LoginRequiredMixin, ListView):
model = Car
def get_queryset(self):
return Car.objects.filter(user=self.request.user)
def get_context_data(self, **kwargs):
context = super(CarList, self).get_context_data(**kwargs)
context['car_list'] = context['object_list'].filter(user=self.request.user)
return context
class WashService(LoginRequiredMixin, CreateView):
model = Wash
form_class = WashServiceForm
template_name = 'service_app/standard_wash_form.html'
success_url = reverse_lazy('service_app:wash_review')
class WashReview(LoginRequiredMixin, TemplateView):
model = Wash
urls:
app_name = 'service_app'
urlpatterns = [
path('car-list/', CarList.as_view(), name='car_list'),
path('<int:id>/select_wash/', WashService.as_view(), name='wash_service'),
path('<int:pk>/review', WashReview.as_view(), name='wash_review'),
]
For class based views you can use self.kwargs.get('parameter')
You also need to add get_success_url to your class WashService
class WashService(LoginRequiredMixin, CreateView):
model = Wash
form_class = WashServiceForm
template_name = 'service_app/standard_wash_form.html'
def get_success_url(self):
return reverse('service_app:wash_review',args=[self.object.id])

Django list data from dfiferent models in List View with correct ordering

Heys guys.. I am making a Django project which is a simple clone of Twitter.. Got the idea from Justin Mitchell's Udemy course..
So i implemented a Tweet model and a Retweet model which has ForeignKey to the original Tweet and the User..
The thing is that in the homepage i want both the Tweets and Retweets to show and in the order they were created..
I am using Django Rest Framework for the CRUD functionality of Tweet using ModelViewSet
Any idea on how i achieve that using Rest Framework or if that isn't possible could you please give me some other idea..
Thank you in advance..
models.py
class Tweet(models.Model):
content = models.CharField(max_length=140)
user = models.ForeignKey(User, on_delete=models.CASCADE)
created_on = models.DateTimeField(auto_now_add=True)
updated_on = models.DateTimeField(auto_now=True)
class Meta:
ordering = "-created_on", "content", "user",
def __str__(self):
return self.content
def get_absolute_url(self):
return reverse("tweet_api:tweet-detail", args=[self.id])
class Retweet(models.Model):
tweet = models.ForeignKey(Tweet, on_delete=models.CASCADE, related_name="retweet")
user = models.ForeignKey(User, on_delete=models.CASCADE)
created_on = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = "-created_on", "user",
def __str__(self):
return self.tweet.content
serializers.py
class TweetSerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=True)
created_on = serializers.SerializerMethodField()
date_display = serializers.SerializerMethodField()
class Meta:
model = models.Tweet
fields = "id", "content", "created_on", "date_display", "user",
def get_created_on(self, obj):
return obj.created_on.strftime("%I:%M %p - %d %b %Y")
def get_date_display(self, obj):
obj_date = obj.created_on
days = (timezone.datetime.now() - obj_date).days
if days > 0:
return obj_date.strftime("%d %b")
else:
return naturaltime(obj_date)
class RetweetSerializer(serializers.ModelSerializer):
tweet = TweetSerializer()
user = UserSerializer(read_only=True)
date_display = serializers.SerializerMethodField()
class Meta:
model = models.Retweet
fields = "id", "tweet", "user", "created_on", "date_display",
def get_date_display(self, obj):
obj_date = obj.created_on
days = (timezone.datetime.now() - obj_date).days
if days > 0:
return obj_date.strftime("%d %b")
else:
return naturaltime(obj_date)
views.py
class TweetViewSet(ModelViewSet):
serializer_class = serializers.TweetSerializer
queryset = models.Tweet.objects.all()
pagination_class = DefaultPagination
filter_backends = filters.SearchFilter,
search_fields = "content", "user__username", "user__first_name", "user__last_name",
def perform_create(self, serialiazer):
return serialiazer.save(user=self.request.user)
class RetweetViewSet(ModelViewSet):
serializer_class = serializers.RetweetSerializer
queryset = models.Retweet.objects.all()
pagination_class = DefaultPagination
filter_backends = filters.SearchFilter,
search_fields = "tweet__content", "user__username", "user__first_name", "
As Tweet and Retweet data reside on completely different models, what you want to do is not simple with your current model structure. To combine them in a single view, you'd need to override many things on ViewSet, and do the operations like sorting in-memory, which would not be scalable. One possible solution could be to use model inheritance, have a base model for both Tweet and Retweet, and build your serialzier and view set on that model. A model structure like the following could be used:
class Post(models.Model):
"""
Base model for user posts
"""
user = models.ForeignKey(User, on_delete=models.CASCADE)
created_on = models.DateTimeField(auto_now_add=True)
class Tweet(Post):
content = models.CharField(max_length=140)
updated_on = models.DateTimeField(auto_now=True)
class Retweet(Post):
tweet = models.ForeignKey(Tweet, on_delete=models.CASCADE, related_name="retweet")
With these models in-place, you can create a PostSerializer and PostViewSet, and use these only for listing posts, you can keep using Tweet and Retweet views and serializers for creating and updating.

Django Forms: MultipleSelect with choices from another model (Foreign Key)

I'm trying to create a form where the user can select multiple technician. When I add the line technician = forms.SelectMultiple(label='Technicians Involved') to my forms.py I get a big blank box with no data. How can I populate that box with the technicians from the User model?
models.py
class Incident(models.Model):
user_id = models.ForeignKey(User, related_name='user')
technician = models.ForeignKey(User, related_name='technician')
capa = models.CharField('capa number', max_length=9)
forms.py
class IncidentForm(forms.ModelForm):
###################### TRYING! ################################
technician = forms.SelectMultiple(label='Technicians Involved')
###############################################################
class Meta:
model = Incident
fields = [ 'user_id',
'technician',
'capa',
]
views.py
def report_incident(request):
template = "report.html"
if request.method == 'POST':
form = IncidentForm(request.POST)
if form.is_valid():
# Auto capturing logged in user
incident = form.save(False)
incident.user_id = request.user
incident.save()
return HttpResponseRedirect('/incidents/')
else:
form = IncidentForm() #an unbound form
return render(request, template, {'form': form})
************** UPDATE WITH CORRECTIONS I MADE BELOW *********************
models.py
class Incident(models.Model):
user_id = models.ForeignKey(User, related_name='user')
technician = models.ManyToManyField(User, related_name='technician')
capa = models.CharField('capa number', max_length=9)
forms.py
class IncidentForm(forms.ModelForm):
technician = forms.SelectMultiple()
class Meta:
model = Incident
fields = [ 'user_id',
'technician',
'capa',
]
views.py
No changes
admin.py
Changes made to view multiple technicians per incident in the admin interface.
class IncidentAdmin(admin.ModelAdmin):
list_display = ('id',
'user_id',
'capa',
'get_technicians'
)
def get_technicians(self):
return "\n".join([t.technicians for t in obj.technician.all()])
Try this out
class IncidentForm(forms.ModelForm):
technician = forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple(), queryset=User.objects.all())
class Meta:
model = Incident
fields = [
'technician',
'capa',
]

Django REST Framework (DRF): Set current user id as field value

I have model NewsModel and 2 serializers for him:
models.py
class NewsModel(models.Model):
title = models.CharField('Заголовок', max_length=255, help_text='Максимальная длина - 255 символов')
announce = models.TextField('Анонс', help_text='Краткий анонс новости')
author = models.ForeignKey(settings.AUTH_USER_MODEL, help_text='Автор новости', related_name='news')
full_text = models.TextField('Полный текст новости', help_text='Полный текст новости')
pub_date = models.DateTimeField('Дата публикации', auto_now_add=True, default=timezone.now, help_text='Дата публикации')
def comments_count(self):
return NewsComment.objects.filter(news=self.id).count()
def get_author_full_name(self):
return self.author.get_full_name()
class Meta:
db_table = 'news'
ordering = ('-pub_date',)
serilizers.py:
from rest_framework import serializers
from .models import NewsModel
from extuser.serializers import UserMiniSerializer
class NewsReadSerializer(serializers.ModelSerializer):
author = UserMiniSerializer()
class Meta:
model = NewsModel
fields = ('id', 'title', 'announce', 'comments_count', 'reviews', 'author_name')
def get_author_full_name(self, obj):
return obj.get_author_full_name()
class NewsWriteSerializer(serializers.ModelSerializer):
def validate_author(self, value):
value = self.request.user.id
return value
class Meta:
model = NewsModel
I select serializers in the api.py:
class NewsList(ListCreateAPIView):
queryset = NewsModel.objects.order_by('-pub_date')
def get_serializer_class(self, *args, **kwargs):
if self.request.method == 'GET':
return NewsReadSerializer
return NewsWriteSerializer
class Meta:
model = NewsModel
But when I will create NewsModel item, I see Error 400: Bad request [{'author': 'This field is required'}]
How I can set current user id as NewsItem.author value on creating new item?
I don't think you're using the serializer properly. A better practice to set request related data is to override perform_create in your view:
def perform_create(self, serializer):
serializer.save(author=self.request.user)
def perform_update(self, serializer):
serializer.save(author=self.request.user)
and then set your author serializer to read-only:
author = UserMiniSerializer(read_only=True)
this way you can simply use one single NewsSerializer for both read and write actions.
In new DRF you can write
owner = serializers.HiddenField(
default=serializers.CurrentUserDefault()
)
See http://www.django-rest-framework.org/api-guide/validators/#currentuserdefault
In DRF version prior 3 field must be declader with allow_null=True and default=None. DRF don't run checking fields without this params. Result code:
class NewsReadSerializer(serializers.ModelSerializer):
"""
Serializer only for reading.
author field serialized with other custom serializer
"""
author = UserMiniSerializer()
class Meta:
model = NewsModel
fields = ('id', 'title', 'announce', 'comments_count', 'reviews', 'author', 'pub_date',)
class NewsWriteSerializer(serializers.ModelSerializer):
"""
Serializer for creating and updating records.
author here is the instance of PrimaryKeyRelatedField, linked to all users
"""
author = serializers.PrimaryKeyRelatedField(
queryset=User.objects.all(), # Or User.objects.filter(active=True)
required=False,
allow_null=True,
default=None
)
# Get the current user from request context
def validate_author(self, value):
return self.context['request'].user
class Meta:
model = NewsModel
fields = ('title', 'announce', 'full_text', 'author',)
I would try something like this:
your models.py
class NewsModel(models.Model):
title = models.CharField(
'Заголовок', max_length=255,
help_text='Максимальная длина - 255 символов')
announce = models.TextField('Анонс',
help_text='Краткий анонс новости')
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
help_text='Автор новости', related_name='news')
full_text = models.TextField(
'Полный текст новости',
help_text='Полный текст новости')
pub_date = models.DateTimeField(
'Дата публикации', auto_now_add=True,
default=timezone.now, help_text='Дата публикации')
def comments_count(self):
return NewsComment.objects.filter(news=self.id).count()
def get_author_full_name(self):
return self.author.get_full_name()
class Meta:
db_table = 'news'
ordering = ('-pub_date',)
serializers.py
(ref.: http://www.django-rest-framework.org/api-guide/validators/#currentuserdefault)
from <yourapp>.models import NewsModel
from rest_framework import serializers
class NewsModelSerializer(serializers.ModelSerializer):
author = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model = NewsModel
Also you should set settings.py to something like this:
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',)
}