How to add custom method in django forms - python

I am new to django, and I am creating a vacation application. I want to be able to when I create a new trip, the user that created the trip becomes a member of that trip.
here is my models.py file:
class Trip(models.Model):
trip_name = models.CharField(max_length=255,unique=False)
start_date = models.DateField(default=datetime.date.today)
end_date = models.DateField(default=datetime.date.today)
slug = models.SlugField(allow_unicode=True,unique=True)
members = models.ManyToManyField(User,through='TripMember')
def __str__(self):
return self.trip_name
def save(self,*args,**kwargs):
self.slug = slugify(self.trip_name)
super().save(*args,**kwargs)
def get_absolute_url(self):
return reverse('trips:single',kwargs={'slug':self.slug})
class Meta:
ordering = ['start_date']
class TripMember(models.Model):
trip = models.ForeignKey(Trip,null=True,related_name='memberships',on_delete=models.SET_NULL)
user = models.ForeignKey(User,null=True,related_name='user_trips',on_delete=models.SET_NULL)
def __str__(self):
return self.user.username
class Meta:
unique_together = ('trip','user')
this is my forms.py file:
class TripCreateForm(forms.ModelForm):
class Meta:
fields = ('trip_name','start_date','end_date')
model = Trip
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["trip_name"].label = "Trip Name"
self.fields["start_date"].label = "Start Date"
self.fields["end_date"].label = "End Date"
here is my views.py file:
class CreateTrip(CreateView):
form_class = TripCreateForm
template_name = 'trips/trip_form.html'
and my trip_form.html page:
<form action="{% url 'trips:create' %}" method="post" id='tripForm'>
{% csrf_token %}
{% bootstrap_form form %}
<input type="submit" class="btn btn-primary btn-large" value="Create">
</form>
Where would I put the code to set the user as a tripmember and why?Also, if there is a better way to have set this up please let me know! I was gonna put it in the save part of the model but I am not quite sure if that is correct. Thanks!

You can override the form_valid() method of the CreateTrip class in your view:
def form_valid(self, form):
"""If the form is valid, save the associated model."""
self.object = form.save()
# add the current user to the members list of the trip
user = self.request.user
self.object.members.add(user)
return super().form_valid(form)

Related

How can i display a Foreign Key on a select tag in my django template?

I'm trying to display some information on a SELECT tag on my template that passes through a for loop and can't seem to find a how to. The examples I find on the internet aren't dynamics (from another table) and I'm really struggling on that. Bellow are my codes, hope you guys can help me on this.
This is my models.py
class Books(models.Model):
[...]
category_cod_category = models.ForeignKey(
'Category',
on_delete = models.CASCADE
)
class Meta:
db_table = 'Books'
class Category(models.Model):
category_name = models.CharField(
max_length = 45
)
def __str__(self):
return '%s' % self.category_name
class Meta:
db_table = 'Category'
This is my views.py and this bit of code is from another template where I have a button for editing.
def edit(request,id):
book = Books.objects.get(id=id)
return render(request,'edit.html',{'edit_book':book})
This is my template edit.html where i need the category of the books to be displayed. Every other field is OK.
<form method="POST" class="post-form" action="/update/{{ book.id }}">
{% csrf_token %}
[...]
<div class="container">
<label>Categoria do Livro:</label>
<select class="selectpicker form-control" data-live-search="true">
{% for books in Books %}
<option value="{{ edit_livro.category_cod_category}}">{{ edit_livro.category_cod_category}}</option>
{% endfor %}
</select>
<div>
[...]
Can you guys help me? Or send me a place where I can learn how to solve this?
Sorry for any misspellings, I'm not a native english writer.
Thanks!
EDIT:
I'm using ModelForms for my insert template, It's listed below:
class BooksForm(forms.ModelForm):
class Meta:
model = Books
fields = [ ...,'category_cod_category', ... ]
# Step:1
# Create url for views (listing, create, update)
urls.py
path(r'book-listing/', BookListView.as_view(), name='book-info-listing'),
path(r'book-info-create/', BookCreateView.as_view(), name='create-book-info'),
path(r'book-info-edit/<int:id>', BookUpdateView.as_view(), name='edit-book-info'),
# Step:2
# Create form for your model
forms.py
# You just need to set the queryset for your field of form, if you want to get the select tag for all your category
class BooksForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.fields['category_cod_category'].queryset = Category.objects.all()
class Meta:
model = Books
fields = [ ...,'category_cod_category', ... ]
# Step:3
# Create views for your model
views.py
# For Listing a book info
class BookListView(generic.ListView):
model = Books
def get_queryset(self):
books = self.model.objects.all()
return books
# For Creating a new book
class BookCreateView(generic.CreateView):
model = Books
form_class = BookForm
template_name = 'project_app/book-form.html'
def form_valid(self, form):
form.save(self.request)
return redirect('book-info-listing')
# For Editing or updating a book
class BookUpdateView(generic.UpdateView):
model = Books
form_class = BookForm
template_name = 'project_app/book-form.html'
success_url = reverse_lazy('book-info-listing')
# get the object
def get_object(self, *args, **kwargs):
book_info = get_object_or_404(Books, pk=self.kwargs['id'])
return book_info
def form_valid(self, form):
form.save(self.request)
return redirect('book-info-listing')
# Step:4 Render form in your template
book-form.html
<form method="POST" class="books_form">
{% csrf_token %}
{{form.category_cod_category}}
</form>

Django SingleObjectMixin: Page not found(404)

I am currently working on a blog for a project, and i want to add in a comment section for each post in the DetailView. My code seems fine, however i keep getting a "Page not Found(404) error". I am thinking it may be my url pattern but i cannot seem to figure out what i am doing wrong.
error given
url.py
urlpatterns[
path('blog/post/<int:pk>/', views.PostDetailView.as_view(),name='post_detail'),
path('blog/post/<int:pk>/comment/', views.MyFormView.as_view(), name='my_form_view_url'),
]
forms.py
class CommentForm(forms.ModelForm):
class Meta():
model = Comment
fields = ('text',)
widgets = {
'text': forms.Textarea(attrs={'class':'ediable medium-editor-textarea'})
}
views.py
class PostDetailView(DetailView):
model = Post
template_name = 'carsforsale/post_detail.html'
def get_context_data(self, **kwargs):
context = super(PostDetailView, self).get_context_data(**kwargs)
context['form'] = CommentForm()
return context
class MyFormView(SingleObjectMixin,FormView):
template_name = 'carsforsale/post_detail.html'
form_class = CommentForm
model = Comment
def post(self, request, *args, **kwargs):
self.object = self.get_object()
return super().post(request, *args, **kwargs)
def get_success_url(self):
return reverse('carsforsale:post_detail', kwargs={'pk': self.object.pk})
post_detail.html
this is the form inside of the post_detail.html
<form method="POST" action="{% url 'carsforsale:my_form_view_url' pk=post.pk %}">
{% csrf_token %}
<div class="form-group">
{% render_field form.text class="form-control text" rows=3 %}
</div>
<input type="submit" class="btn btn-primary" value="Comment" />
</form>
These are my Post and Comment models
class Post(models.Model):
author = models.ForeignKey('auth.User', on_delete= models.CASCADE)
title = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True, null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
def approve_comments(self):
return self.comments.filter(approved_comment=True)
def get_absolute_url(self):
return reverse("post_list")
def __str__(self):
return self.title
class Comment(models.Model):
post = models.ForeignKey('carsforsale.Post', related_name='comments', on_delete = models.CASCADE)
author = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
approved_comment = models.BooleanField(default=False)
def approve(self):
self.approved_comment = True
self.save()
def get_absolute_url(self):
return reverse("carsforsale:post_detail",kwargs={'pk':self.pk})
def __str__(self):
return self.text
Presumably the pk in the URL for MyFormView refers to the post the comment is going to be attached to. But the view itself has a model attribute of Comment, so that is the model that Django tries to load. The comment with that pk doesn't exist, hence the error.
You will need to override get_object to do the query directly and return the object. You will also need to override form_valid to associate the new comment with that post.
def get_object(self, **kwargs):
return get_object_or_404(Post, pk=self.kwargs["pk"]
def form_valid(self, form):
form.instance.post = self get_object()
return super().form_valid(form)

How can I fill a field with current user in form using CreateView/UpdateView in Django?

I've been looking for this for few days and I couldn't solve it, so here is my problem:
I'm trying to create a product using just CreateView in views.py for now (My idea is to create forms and pass everything to a function in views.py), but there is one field that I want it to be auto-filled with the logged user ('owner'). I've tried using the get_initial in the createView for now, but it doesn't work.
I want to say that this it actually creates a form in which I have to fill all the fields and it works fine, but I want to auto-fill the 'owner' field with the current user logged in.
For now I tried to use the get_initial as I said before, but seems that it does not work. I also tried lots of things that I've seen here, in stackoverflow, but any of them worked for me.
Here I put all the relevant code, but if you need anything else please, ask for it and I'll upload it.
This is my views.py:
# view for the product entry page
class ProductCreate(CreateView):
model = Product
fields = ['owner', 'category', 'tag', 'name', 'content_tweet']
success_url = reverse_lazy('index')
def get_form(self, form_class=None):
form = super(ProductCreate, self).get_form(form_class)
return form
def get_initial(self):
return {
'owner': self.request.user,
}
def dispatch(self, request, *args, **kwargs):
if not request.user.is_superuser or request.user.is_vendor:
raise PermissionDenied()
return super().dispatch(request, *args, **kwargs)
This is the model I'm using in models.py:
class Product(models.Model):
owner = models.ForeignKey(CustomUser, on_delete=models.PROTECT)
name = models.CharField(_('Product Name'), max_length=150)
category = models.ManyToManyField(Category)
tag = models.ManyToManyField(Tag)
content_tweet = models.CharField(_('Content Tweet'), max_length=150, blank=True)
content_abstract = models.CharField(_('Content Abstract'), max_length=3000, blank=True)
canvas_1 = models.FileField(upload_to='documents/canvas_1/', blank=True)
price_tweet = models.IntegerField(_('Tweet Price in Tokens'), default=0)
price_abstract = models.IntegerField(_('Abstract Price in Tokens'), default=50)
price_canvas_1 = models.IntegerField(_('Canvas 1 Price in Tokens'), default=500)
it_exist = models.BooleanField(_('is an existing idea?'), default=False)
is_active = models.BooleanField(
_('active'),
default=True,
help_text=_(
'Designates whether this product should be treated as active. '
'Unselect this instead of deleting products.'
),
)
history = HistoricalRecords()
class Meta:
unique_together = ('name', 'owner')
def get_categories(self):
return ",".join([str(p) for p in self.category.all()])
def get_tags(self):
return ",".join([str(p) for p in self.tag.all()])
def __str__(self):
return self.name
And this is the template I'm using (for now) in my XX.html:
{% for field in form %}
<div class="form-group">
<div >
<span class="text-danger la">{{ field.errors }}</span>
</div>
<label >{{ field.label_tag }}</label>
<div class="col-sm-10">{{ field }}</div>
</div>
{% endfor %}
def form_valid(self, form):
form.instance.owner = self.request.user
return super(ProductCreate, self).form_valid(form)
override form_valid method of CreateView as above

Django - user rate profile

I am a newbie to Django and this question might be an easy one, but it has already been bothering me for a while.
<form method="post" action="/vote/" class="vote_form">
<input type="hidden" id="id_link" name="user_profile" class="hidden_id" value="5" />
<input type="hidden" id="id_voter" name="voter" class="hidden_id" value="3" />
New Score: <input name="score" class="" value="7" />
<button type="submit">Submit</button>
</form>
urls.py
url(r'^vote/$', auth(VoteFormView.as_view()), name="vote"),
views.py
class VoteFormView(FormView):
form_class = VoteForm
def create_response(self, vdict=dict(), valid_form=True):
response = HttpResponse(json.dumps(vdict))
response.status = 200 if valid_form else 500
return response
def form_valid(self, form):
profile = get_object_or_404(UserProfile, pk=form.data['user_profile'])
user = self.request.user
score = form.cleaned_data['score']
prev_votes = Vote.objects.filter(voter=user, profile=profile)
has_voted = (len(prev_votes) > 0)
ret = {"success": 1, "profile": profile, "voter: ": user}
if not has_voted:
# add vote
v = Vote.objects.create(voter=user, profile=profile, score=score)
ret["voteobj"] = v.id
# else response already ranked
return self.create_response(ret, True)
def form_invalid(self, form):
...do something when form invalid...
forms.py
class VoteForm(forms.ModelForm):
class Meta:
model = Vote
models.py
class Vote(models.Model):
voter = models.ForeignKey(User)
profile = models.ForeignKey(UserProfile)
score = models.FloatField(default=10.0)
def __unicode__(self):
return "%s voted %s" % (self.voter.username, self.profile)
class UserProfile(models.Model):
user = models.OneToOneField(User, unique=True)
# Extra attributes
As you can see, I have a simple form with 2 fields: a user_profile (PK of a user profile) and a score. After the form is submitted, it has this error: "form_errors": {"profile": ["This field is required."]} . So it always goes to to form_invalid.
Where am I supposed to get profile object if it is not form_valid?
I tried to redefine VoteForm as below, which does not work either.
class VoteForm(forms.ModelForm):
def clean_profile(self):
profile_pk = self.cleaned_data['profile']
profile = None
try:
profile = UserProfile.objects.get(pk=profile_pk)
except UserProfile.DoesNotExist:
raise forms.ValidationError("User profile doesn't exist.")
return profile
def save(self, *args, **kwargs):
vote = super(VoteForm, self).save(*args, **kwargs)
return vote
class Meta:
model = Vote
Any ideas?
You have to change your hidden input name from user_profile to profile.
Keep in mind this is a poor design since anyone can inject an arbitrary voter id.
Here is how I would do it
class VoteForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop("user")
super(VoteForm, self).__init__(*args, **kwargs)
def save(self, *args, **kwargs):
# manipulate self.user
and then in your view
form = VoteForm(..., user=request.user)
The name of your hidden field is user_profile, but your code refers to it as just profile. Try changing the html of the form so it is named profile.
As an aside you should include the voter id in your form, that should be added in your processing code based on the logged in user. Currently anyone could change the voter hidden field and record a vote for a different user.

Using formset with ContentType

I'm having trouble using the UpdateView for a view consisting of a form and formset.
I have the following models: Item and Picture.
Picture is defined as:
class Picture(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255, blank=False)
content_type = models.ForeignKey(ContentType, verbose_name="content type",
related_name="content_type_set_for_%(class)s")
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey("content_type", "object_id")
I have several models that contain pictures. For example, in the Item model:
class Item(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255, blank=False)
pictures = generic.GenericRelation(Picture)
I have the following ItemCreateForm:
class ItemCreateForm(ModelForm):
def __init__(self, *args, **kwargs):
super(ItemCreateForm, self).__init__(*args, **kwargs)
class Meta:
model = Item
The PictureForm:
class PictureForm(forms.ModelForm):
id = forms.IntegerField(widget=forms.HiddenInput)
def __init__(self, *args, **kwargs):
super(PictureForm, self).__init__(*args, **kwargs)
def save(self):
data = self.cleaned_data
obj = Picture(**data);
# do something to obj
# obj.save()
class Meta:
model = Picture
fields = ['id', 'name']
And the view:
class ItemUpdateView(UpdateView):
form_class = ItemCreateForm
template_name = 'item/new.html'
model = Item
success_url = '/items/'
def get_context_data(self, **kwargs):
context = super(ItemUpdateView, self).get_context_data(**kwargs)
item = context['object']
# Dont' create any extra forms when showing an update view
PictureFormSet = formset_factory(PictureForm, extra=0)
return {'form': kwargs['form'],
'picture_formset': UploadFormSet(initial = [ model_to_dict(a) for pic in item.pictures.all()])}
def post(self, request, *args, **kwargs):
self.object = self.get_object()
item_form = ItemCreateForm(request.POST, instance=self.object)
if item_form.is_valid():
item = item_form.save(commit=False)
item.save()
# How do update the pictures?
This is my urls.py:
url(r'^items/(?P<pk>\d+)/update/$', ItemUpdateView.as_view(), name='item_update')
The template:
<form action="" method="post" enctype="multipart/form-data">
{% for field in form %}
# do something
{% endfor %}
{{ picture_formset.management_form }}
{% for form in picture_formset.forms %}
# do something
{% endfor %}
<input name="commit" type="submit" value="Submit" />
</form>
I'm new to Django.
The user can dynamically(via jQuery) add/remove pictures through the Picture form in the single template that is used to display the item and multiple pictures.
1 I had to include the id as a hidden field for the picture, otherwise the pictures will be inserted instead of an Update. QN: Is there a better way to do this?
2 How do I update the picture model? Currently request.POST doesn't have all the fields in the model, thus the model is complaining of NULL fields? I'm totally at lost how to deal with formset in an UpdateView and is not the main form, like a simple example of UpdateView with the pk in the url.
PictureFormSet = formset_factory(PictureForm)
picture_formset = PictureFormSet(request.POST, request.FILES)
for picture_form in picture_formset.forms:
picture_form.save()

Categories

Resources