I'm learning django and Wagtail and currently looking to use Streamfield for menus and submenus for a personal site. I modified some code I found via a tutorial video and would want to create a basic menu and submenu. All fields show OK in the Wagtail CMS. The problem is that the pagechooserpanel is not showing in the CMS. I'm not sure what's wrong and how to fix it. I'd really appreciate any help with this
class MenuItemBlock(blocks.StructBlock):
link_title = blocks.CharBlock(
blank=True,
null=True,
max_length=50
)
link_url = blocks.CharBlock(
max_length=500,
blank=True
)
link_page = models.ForeignKey(
"wagtailcore.Page",
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name="+",
)
open_in_new_tab = blocks.BooleanBlock(default=False, blank=True)
panels = [
FieldPanel("link_title"),
FieldPanel("link_url"),
PageChooserPanel("link_page"),
FieldPanel("open_in_new_tab"),
]
#property
def link(self):
if self.link_page:
return self.link_page.url
elif self.link_url:
return self.link_url
return '#'
#property
def title(self):
if self.link_page and not self.link_title:
return self.link_page.title
elif self.link_title:
return self.link_title
return 'Missing Title'
class Meta:
icon = 'plus'
label = 'Menu Item'
# template = 'menu_item.html'
class SubMenuBlock(blocks.StreamBlock):
sub_menu_items = MenuItemBlock()
panels = [
FieldPanel("sub_menu_items")
]
class Meta:
icon = 'list-ul'
label = 'Sub Menu'
# template = 'sub_menu.html'
#register_snippet
class Menu(ClusterableModel):
name = models.CharField(max_length=255)
slug = AutoSlugField(populate_from="name", editable=True)
menu_items = StreamField([
('MenuItem', MenuItemBlock()),
('SubMenu', SubMenuBlock()),
], blank=True, use_json_field=True)
panels = [
MultiFieldPanel([
FieldPanel("name"),
FieldPanel("slug"),
], heading="Menu"),
FieldPanel('menu_items', heading='Menu Items')
]
def __str__(self):
return self.name
ForeignKey is only valid on Django models, not StreamField blocks. Also, panels definitions are not valid on blocks and will be ignored.
You should use PageChooserBlock instead:
link_page = blocks.PageChooserBlock(required=False)
Additionally, the keyword arguments blank and null are not valid on CharBlock - the equivalent is required=False. See the StreamField blocks reference in the Wagtail docs.
Related
I am still new to django and have encountered this issue, the situation is like this, I have a profile model, on which I have 2 views ViewProfile and EditProfile inheriting from DetailView and UpdateView respectively. when I edit the profile page, it doesn't get me to the profile page instead it gave the error:
Reverse for 'profile' with keyword arguments '{'id': 9}' not found. 1 pattern(s) tried: ['profile/(?P<pk>[^/]+)/\\Z']
even though I have checked in the python shell, the profile with id:9 is indeed profile with name muham see below
>>> Profile.objects.all()
<QuerySet [<Profile: huzaifa>, <Profile: another1>, <Profile: muham>]>
>>> p1 = Profile.objects.get(name='muham')
>>> p1.id
9
>>>
but still its not showing,
I have overridden the get_success_url to get me to the profile page:
class EditProfile(LoginRequiredMixin, UpdateView):
model = Profile
fields = ('name', 'address', 'phone_no',)
template_name = 'blog_app/edit_profile.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['user'] = self.request.user
return context
def get_success_url(self):
id = self.request.user.profile.id
return reverse_lazy('profile', kwargs={'id': id})
my model is below:
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True, blank=True)
name = models.CharField(max_length=60, blank=True, null=True)
address = models.CharField(max_length=400, null=True, blank=True)
# image
phone_no = models.CharField(max_length=40, null=True, blank=True)
def __str__(self):
return str(self.user)
class ViewProfile(LoginRequiredMixin, DetailView):
model = Profile
template_name = 'blog_app/profile.html'
context_object_name = 'profile'
urls.py:
urlpatterns = [
path('', ListBlog.as_view(), name='home'),
path('my-blogs/', MyBlog.as_view(), name='my-blogs'),
path('create-blog/', CreateBlog.as_view(), name='blog-create'),
path('blog-detail/<int:pk>/', BlogDetail.as_view(), name='blog-detail'),
path('edit-blog/<int:pk>/', EditBlog.as_view(), name='blog-edit'),
path('delete-blog/<int:pk>/', DeleteBlog.as_view(), name='blog-delete'),
path('profile/<str:pk>/', ViewProfile.as_view(), name='profile'),
path('edit-profile/<str:pk>/', EditProfile.as_view(), name='edit-profile'),
]
I have found my error, it was because DetailView uses pk or slug, on the urlconf arguments, and before i have added str:id instead of str:pk, and also in the
def get_success_url(self):
id = self.request.user.profile.id
return reverse_lazy('profile', kwargs={'id': id})
i had kwargs={'id':id} instead of {"pk": id}. so that was my issue, and it was resolved after that.
--WHY I CANT TO ACCESS TO ID OF USER AND PRODUCT WITH user__id and product__id?
i have error: (The view store.views.submit_review didn't return an HttpResponse object. It returned None instead).
<pre><code>
i define some code for rating post for any user user
--#views
def submit_review(request, product_id):
url = request.META.get('HTTP_REFERER')
if request.method == 'POST':
try:
reviews = ReviewRating.objects.get(user__id=request.user.id,Product__id=product_id) #my problem is here
form = Reviewform(request.POST, instance=reviews)
form.save()
messages.success(request, 'Thank you!your review has been updated.')
return redirect(url)
except ReviewRating.DoesNotExist:
form = Reviewform(request.POST)
if form.is_valid():
data = ReviewRating()
data.subject = form.cleaned_data['subject']
data.review = form.cleaned_data['review']
data.rating = form.cleaned_data['rating']
data.ip = request.META.get['REMOTE_ADDR']
data.product_id = product_id
data.user_id = request.user.id
data.save()
messages.success(request, 'Thank you! Your review has been submitted')
return redirect(url)
</code></pre>
this section i define model.I checked this part it work correctly
define model for views in my app
#models
class ReviewRating(models.Model):
Product = models.ForeignKey(product, on_delete=models.CASCADE)
user = models.ForeignKey(Account, on_delete=models.CASCADE)
subject = models.CharField(max_length=100, blank=True)
review = models.TextField(max_length=500, blank=True)
rating = models.FloatField()
ip = models.CharField(max_length=20, blank=True)
status = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.subject
this part i define url
#urls
define path for html and views
urlpatterns = [
path('submit_review/int:product_id/',views.submit_review,name='submit_review')
]
your model is not clear. It would be better if you could atleast share your full model.py file
if you provide the objective of your service with models.py, serializers.py, views.py I think I will be able to help more specifically
till now, one issue i've found in the the url section is that you need to add < > on int:product_id.
it should be,
urlpatterns = [
path('submit_review/<int:product_id>/',views.submit_review,name='submit_review')
]
I am working on a blog project with Django using the django-quill-editor. Like the documentation says, I have imported QuillField and added it to my model as shown below. I have also added it to my admin.
Now the problem is that whenever i insert an image into the content field of the editor, The image is displayed fully on the details page of the blog. The image is not contained in any div, making it look out of proportion. I realize this is happening because the bootstrap class 'img-fluid' is not applied to the image. I want the image to fit into the text area. So I want know how to customize django quill editor to do this. I can't seem to find it in any documentation for django.
models.py
from django_quill.fields import QuillField
# Create your models here.
class Post(models.Model):
STATUS_CHOICES = (
('draft', 'Draft'),
('published', 'Published'),
)
title = models.CharField(max_length=250, unique=True)
slug = models.SlugField(max_length=250, unique_for_date='publish')
author = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, related_name='blog_posts')
content = QuillField()
publish = models.DateTimeField(default=timezone.now)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
banner = models.ImageField(upload_to='banners/', blank=True, null=True, default='/banners/blog-1.jpg')
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft')
def __str__(self):
return self.title
admin.py
from django.contrib import admin
from .models import Post, Comment
# Register your models here.
#admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ('title', 'slug', 'author', 'publish', 'status')
list_filter = ('status', 'created', 'publish', 'author')
search_fields = ('title', 'body')
prepopulated_fields = {'slug': ('title',)}
raw_id_fields = ('author',)
date_hierarchy = 'publish'
ordering = ('status', 'publish')
I have two models (customer, movie) and I would like to return (movie_name, customer_name, id) when I hit the URL (api/customer/1) and when I hit the URL (api/customer/1/movies) just wanted the movie names alone. How can we achieve this ?
models.py
class Customer(models.Model):
name = models.CharField(max_length=200, null=True)
class Movie(models.Model):
movie_name = models.CharField(max_length=200, null=True)
customer = models.ForeignKey(Customer, null=True, on_delete=models.SET_NULL)
serializers.py
class CustomerSerializer(serializers.ModelSerializer):
class Meta:
model = Customer
fields = ('id', 'name')
class MovieSerializer(serializers.ModelSerializer):
class Meta:
model = Movie
fields = '__all__'
urls.py
urlpatterns = [
path('admin/', admin.site.urls),
url(r'^api/customers/$', CustomerSerializer.as_view(), name='customers'),
]
Note:
At the moment, when I hit the URL (api/customers) it returns the id, name of all the customers. Now, I would like to know, when I hit the URL (api/customer/1) how to list the same information along with movie names and when I hit the URL (api/customer/1/movies) how to just return the movie names alone?
You will have to write two urls for this purpose but you can do this with one view and serializer like this
Urls
urlpatterns = [
path('admin/', admin.site.urls),
path('api/customers/<int:id>/', CustomerMovieView.as_view(check=True)),
path('api/customers/<int:id>/movies/', CustomerMovieView.as_view(check=False)),
]
Views and Serializers
from rest_framework import generics, response, serializer
class MovieSerializer(serializers.ModelSerializer):
customer_name = serializers.SerializerMethodField()
def get_customer_name(self, instance):
return instance.customer.name
class Mete:
model = Movie
fields = '__all__'
def to_representation(self, instance):
data = super().to_representation(instance)
if not self.context('check'):
data.pop('customer_name', None)
data.pop('customer', None)
return data
class CustomerMovieView(generics.GenericAPIView):
serializer_class = MovieSerializer
check = True
def get_serializer_context(self):
context = super().get_serializer_context()
context.update({'check': self.check})
return context
def get(self, request, *args, **kwargs):
id = kwargs.get('id')
movie = Movie.objects.get(id=id)
serialized = self.get_serializer(movie)
return response.Respoonse(serialized.data)
What the simplest logic i can add in class HotelDetailView(DetailView) so users can comment on a particular hotel detail page. And it capture user too.
models.py
class Hotel(models.Model):
name = models.CharField(max_length=150)
owner = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
image = models.ImageField(upload_to=upload_location, null=True, blank=True)
class CommentOnHotel(models.Model):
hotel = models.ForeignKey(Hotel, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
content = models.TextField(max_length=200)
published = models.DateField(auto_now_add=True)
def __str__(self):
return '{} - {}'.format(self.hotel.name, self.user.email)
Forms.py
class CommentOnHotelForm(forms.ModelForm):
class Meta:
model = CommentOnHotel
fields = ['content']
views.py
class HotelDetailView(DetailView):
model = Hotel
........
actually i gave you the better approach for that, but if you want to make all logic and functions at DetailView which is not recommended, here is the solution, you need to override POST function of DetaiView:
class HotelDetailView(DetailView):
model = Hotel
def post(self, request, *args, **kwargs):
# get the hotel object
hotel = self.get_object()
#check for validation of form
form = CommentOnHotelForm({
"hotel": hotel,
"user" :request.user
"comment": request.POST.comment
})
if form.is_valid():
form.save()
#choose where you want to redirect