I have created a object which I am trying to save in Django admin in myapp>Hello. But the object does not get created under 'Hello' when I run the server. How can I fix it? I have also registered my models in admin.py.
models.py:
from django.db import models
from django.contrib.auth.models import User
class Foo(models.Model):
foo_id = models.CharField(max_length=10)
class Hello(models.Model):
user = models.ForeignKey(User, models.DO_NOTHING)
foo_id = models.ForeignKey('Foo', models.DO_NOTHING, db_column='foo_id')
foo_text = models.CharField(max_length=500, default="Hello!")
views.py
from django.shortcuts import render,HttpResponse,redirect
from django.shortcuts import render
from django.contrib.auth.decorators import login_required
from .models import User,Foo, Hello
from django.contrib.auth import settings
#login_required
def home(request):
return render(request, 'index.html')
#login_required
def sendhello() :
Foos=Foo.objects.all()
for foo in foos:
#Hello(user=user, foo_text='hello there', foo_id=foo).save()
xyz, obj=Hello.objects.get_or_create(user=user, foo_text='hello there', foo_id=foo)
if xyz is True:
obj.save()
#login_required
def helloxyz(request):
user = User.objects.get(id=request.session['id'])
hellos=Hello.objects.filter(user_id=user)
print(hellos)
hellos_list=[]
for hello in hellos:
print(hello.hello_text)
hellos_list.append(hello.hello_text)
hellos_list.reverse()
print(hellos_list)
return render(request,'index.html',{'hellos': hellos_list,})
First of all, your sendhello function is not a view, so it does not get called when you view your site. I will for now just assume you have a JS login dialogue or someting you get inputs from. SO you want to generate DB entries from these inputs that are present (?) in your DB. You could do this by simply calling your generating function from within a view class or function (I recommend to put such non-view helper functions in a seperate file and import them in your view) or outside of views with e.g. django cron.
The simplest would be to just create a new file utilis.py, import them in your views with import myapp.utils and then call your function at the top of your site view.
Why a sperate file? It is just for better readability, is cleaner when someone else looks at your code and swapping out helpers is a bit easier.
If you have regularly occuring tasks that should best be executed independant from the user loading a specific page, take a look at the django_crontab module, it is really handy. You need to be on a Linux system dough.
I hope that answered your question
Related
hi everyone i'm trying to make a blog and i want to associate a user to post .. and to comment , it's a multi-user blog and i don't know how to do it , any help guys ? !
here is the model file :
from django.db import models
from django.utils import timezone
from django.conf import settings
from django.utils.text import slugify
# Create your models here.
#this is for categories
class Category(models.Model):
title=models.CharField(max_length=100,default='')
def __str__(self):
return self.title
#this is where a user can create his own gigs
class Gigposter(models.Model):
title=models.CharField(default='',max_length=100,blank=False)
user=models.OneToOneField(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,null=False)
categories=models.OneToOneField(Category,on_delete=models.PROTECT,default='',null=False)
published_at=models.DateTimeField(auto_now_add=True)
description=models.TextField(default='',max_length=None,blank=False)
mainphoto=models.ImageField(default='')
photo=models.ImageField()
def __str__(self):
return self.title
#this is where a user can comment and say what he thinks about others work
class Comment_area(models.Model):
user=models.OneToOneField(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,blank=False)
comment=models.TextField(max_length=None,default='')
commented_at=models.DateTimeField(auto_now_add=True)
and the views file is empty as you can see :
from django.shortcuts import render
# Create your views here.
Wouldn't recommend using a OneToOneField here, as it tells Django the user is tied to exactly one comment/post (when, a user is likely to post more than once).
You could use models.ForeignKey:
from django.contrib.auth.models import User
class Gigposter(models.Model):
# Other properties...
user = models.ForeignKey(
'User'
on_delete=models.CASCADE
)
If you want to automatically associate a user to a post when working on the admin page, you should redefine save_model method of your model. This method describes everything what should be done when you save your model. In your case you should add something like
class GigposterAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
obj.user = `request.user`
super().save_model(request, obj, form, change)
admin.site.register(Gigposter, GigposterAdmin)
to your admin.py. You should also exclude the user field from fieldset in GigposterAdmin. See this for reference.
If you need to identify user in your views, you can always use request.user. In particular, you can pass it as a part of the context for generating a view. Hope this helps.
I have a wagtail app in which I need to return the current page's url. The code for my model.py of my app is as follows
import urllib
from django.conf import settings
from django.db import models
from wagtail.core.models import Page
from django.http import HttpResponse
class PageUrl(Page):
def returnPageUrl(self):
page_url = """ i need the page url here """
return page_url
A Page model has a field url_path. You thus can access the page URL with:
class PageUrl(Page):
def returnPageUrl(self):
return self.url_path
or you can make use of full_url to obtain the full url (including the hostname, etc.):
class PageUrl(Page):
def returnPageUrl(self):
return self.full_url
That being said, since it is already a field, I think there is not much use to define an extra method for this.
I am currently building a small project using Django, I have noticed a problem that a logged in user was getting access to the other users page by simply changing the id in the url i.e
This is the url of currently logged in user
http://localhost:8000/home/myBooks/7/
by changing that id from 7 to 6
i.e
http://localhost:8000/home/myBooks/6/
He was getting access to that page,I have used #login_required for functional based views and LoginRequiredMixin for class based views ,but they are not helping, what else I need to do to prevent this problem?
My app/views.py:
from django.shortcuts import render,redirect
from django.http import HttpResponse
from django.views.generic.edit import FormView
from . forms import BookForm
from django.contrib.auth.models import User
from . models import UserBooks
from django.contrib.auth.models import User
from django.views import generic
from django.contrib.auth.decorators import login_required
from .models import UserBooks
from django.shortcuts import get_object_or_404
from django.contrib.auth.mixins import LoginRequiredMixin
#login_required
def HomeView(request):
return render(request,'home/homepage.html')
class BookDetailsView (LoginRequiredMixin,generic.DetailView):
model=UserBooks
template_name='home/bookdetails.html'
class BooksView (LoginRequiredMixin,generic.DetailView):
model=User
template_name='home/mybooks.html'
#login_required
def addBooks(request):
if (request.method=='POST'):
form=BookForm(data=request.POST)
if(form.is_valid()):
u=UserBooks()
u.book_name=form.cleaned_data['book_name']
u.book_author=form.cleaned_data['book_author']
u.book_ISBN=form.cleaned_data['book_ISBN']
u.book_status=True
u.book_genre=form.cleaned_data['book_genre']
u.username=request.user.username
u.user_id = User.objects.get(username=request.user.username)
u.save()
return redirect('/')
else:
form = BookForm()
return render (request,'home/addbooks.html',{'form':form})
my apps/models.py:
from django.db import models
from django.contrib.auth.models import User
class UserBooks(models.Model):
user_id = models.ForeignKey(User,on_delete=models.CASCADE,null=True)
username = models.CharField(max_length=200)
book_name = models.CharField(max_length=200)
book_author = models.CharField(max_length=200)
book_ISBN=models.CharField(max_length=200)
book_genre = models.CharField(max_length=200)
book_status=models.BooleanField(default=False)
class Meta:
unique_together = (("username", "book_ISBN"),)
def __str__(self):
return self.book_name
my apps/urls.py:
from django.urls import path
from . import views
app_name='home'
urlpatterns=[
path('',views.HomeView,name='home'),
path('addBooks/',views.addBooks,name='addBooks'),
path('myBooks/<int:pk>/',views.BooksView.as_view(),name='myBooks'),
path('<int:pk>/', views.BookDetailsView.as_view(), name='myBooks'),
]
If your view should always show the detail for the current user, don't put the ID in the URL at all; get the logged-in user directly within the view.
class BooksView(LoginRequiredMixin, generic.DetailView):
model = User
template_name ='home/mybooks.html'
def get_object(self):
return self.request.user
...
path('myBooks/',views.BooksView.as_view(),name='myBooks'),
class BooksView(LoginRequiredMixin, DetailView):
...
def get(self, request, *args, **kwargs):
current_user = User.objects.get(id=self.request.user.pk)
if current_user.pk == kwargs['pk']:
return HttpResponseRedirect('/')
else:
return HttpResponseRedirect('profile-url')
Here I assume that if you are logged in user and you try to check another user profile by giving id in url. So I add a get method which will check is requested URL id is for the current user (books/7/ is 7 is current user id) if not then redirect to an URL, for example, otherwise redirects to another url. You can get some idea. This may not help you exactly.
If you have just started to develop the app, then it is okay to use pks inside of urls. However, when it comes to a real working app it can lead to some security problems.
As you wrote, one can simply change the url and get some private data.
Other problems can be:
The number of users in the database can be easily counted by iteration through your urls.
The user can be easily detected by his id. Knowing it one can easily get some private data.
If you will decide to change ids in your db, then all the external links will be broken...and etc.
Considering that I suggest an approach in which you use ids internally. For external usage (urls, links) you can use uuids.
To do that you just need additional field into your model:
import uuid
uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
And here is the example url:
url(r'^myBooks/(?P<user_uuid>\b[0-9A-Fa-f]{8}\b(-\b[0-9A-Fa-f]{4}\b){3}-\b[0-9A-Fa-f]{12}\b)/$',
After you switch to uuids it will be almost impossible to "hack" the url.
I am building an app in django to keep track of social events. I am trying to handle recurring events through django-simple-events package. Issue is when I try to save any data from the admin site, it says record successfully added but when I check my model I don't see any record. When I click the object from recent activity I get an error 404 with the following message study object with primary key u'None' does not exist.
models.py
from django.db import models
from django.contrib.contenttypes import generic
from events.models import Event
class Study(models.Model):
study_event_name = models.CharField(max_length=140)
events = generic.GenericRelation(Event)
location = models.CharField(max_length=140)
def save(self):
super(Study, self).save
def __unicode__(self):
return self.study_event_name
admin.py
from django.contrib import admin
from study.models import Study
from events.admin import EventInline
# Register your models here.
class StudyAdmin(admin.ModelAdmin):
inlines = EventInline,
admin.site.register(Study, StudyAdmin)
Can anyone help me in figuring out what I am doing wrong here.
I would like to add some extra fields to pages in django-cms (in django admin panel). How do this in the simplest way?
Create a new app (called extended_cms or something) and in models.py create the following:
from django.db import models
from django.utils.translation import ugettext_lazy as _
from cms.models.pagemodel import Page
class ExtendedPage(models.Model):
page = models.ForeignKey(Page, unique=True, verbose_name=_("Page"), editable=False, related_name='extended_fields')
my_extra_field = models.CharField(...)
then create an admin.py:
from models import ExtendedPage
from cms.admin.pageadmin import PageAdmin
from cms.models.pagemodel import Page
from django.contrib import admin
class ExtendedPageAdmin(admin.StackedInline):
model = ExtendedPage
can_delete = False
PageAdmin.inlines.append(ExtendedPageAdmin)
try:
admin.site.unregister(Page)
except:
pass
admin.site.register(Page, PageAdmin)
which will add your extended model to as an inline to any page you create. The easiest way to access the extended model setttings, is to create a context processor:
from django.core.cache import cache
from django.contrib.sites.models import Site
from models import ExtendedPage
def extended_page_options(request):
cls = ExtendedPage
extended_page_options = None
try:
extended_page_options = request.current_page.extended_fields.all()[0]
except:
pass
return {
'extended_page_options' : extended_page_options,
}
and now you have access to your extra options for the current page using {{ extended_page_options.my_extra_field }} in your templates
Essentially what you are doing is creating a separate model with extra settings that is used as an inline for every CMS Page. I got this from a blog post previously so if I can find that I'll post it.
EDIT
Here is the blog post: http://ilian.i-n-i.org/extending-django-cms-page-model/
There is an official way to extend the page & title models, I highly recommend this official documentation:
Extending the page & title models from docs.django-cms.org
I also highly recommend using a placeholder if you can, since writing this answer, I now prefer creating a placeholder for the use case of cover images. (You can even get just the image URL in your template if you want to).
Summary of the link:
Create a subclass of PageExtension in your models.py file and register it:
class IconExtension(PageExtension):
image = models.ImageField(upload_to='icons')
extension_pool.register(IconExtension)
Create also a subclass of PageExtensionAdmin in your admin.py file and register it:
class IconExtensionAdmin(PageExtensionAdmin):
pass
admin.site.register(IconExtension, IconExtensionAdmin)
Finally, to make it accessible from the toolbar, create a subclass of ExtensionToolbar in cms_toolbars.py and register it:
#toolbar_pool.register
class IconExtensionToolbar(ExtensionToolbar):
model = IconExtension
def populate(self):
current_page_menu = self._setup_extension_toolbar()
if current_page_menu:
page_extension, url = self.get_page_extension_admin()
if url:
current_page_menu.add_modal_item(_('Page Icon'), url=url,
disabled=not self.toolbar.edit_mode)
The official documentation goes into more detail and explanation.
There is an open GitHub issue on adding support for adding elements to the normal and advanced "page settings" dialogues.
There's also a way to do this without using an inline, and having the fields anywhere on the Page form. For example, I have a custom setting for "color scheme" that I wanted to be under the "Basic Settings" fieldset. This can be done by overriding the ModelForm and the ModelAdmin's fieldsets. Also, I opted for a OneToOne field instead of a ForeignKey, for simplicity's sake.
models.py:
from django.db import models
from cms.models.pagemodel import Page
from django.conf import settings
class PageCustomSettings(models.Model):
page = models.OneToOneField(Page, editable=False,
related_name='custom_settings')
color_scheme = models.CharField(blank=True, choices=settings.COLOR_SCHEMES,
max_length=20)
admin.py:
from django import forms
from django.conf import settings
from django.contrib import admin
from cms.admin.pageadmin import PageAdmin, PageForm
from cms.models.pagemodel import Page
from web.models import PageCustomSettings
color_scheme_choices = (('', '---------'),) + settings.COLOR_SCHEMES
class CustomPageForm(PageForm):
color_scheme = forms.ChoiceField(choices=color_scheme_choices,
required=False)
def __init__(self, *args, **kwargs):
# make sure that when we're changing a current instance, to set the
# initial values for our custom fields
obj = kwargs.get('instance')
if obj:
try:
opts = obj.custom_settings
kwargs['initial'] = {
'color_scheme': opts.color_scheme
}
except PageCustomSettings.DoesNotExist:
pass
super(CustomPageForm, self).__init__(*args, **kwargs)
def save(self, commit=True):
# set the custom field values when saving the form
obj = super(CustomPageForm, self).save(commit)
try:
opts = PageCustomSettings.objects.get(page=obj)
except PageCustomSettings.DoesNotExist:
opts = PageCustomSettings(page=obj)
opts.color_scheme = self.cleaned_data['color_scheme']
opts.save()
return obj
PageAdmin.form = CustomPageForm
PageAdmin.fieldsets[1][1]['fields'] += ['color_scheme']
admin.site.unregister(Page)
admin.site.register(Page, PageAdmin)
I've got here via Google and the answers got me on the right track for Django CMS 3 Beta. To extend the page model and hook your extension into the toolbar, you can follow along the official documentation:
http://django-cms.readthedocs.org/en/latest/how_to/extending_page_title.html
Access value in template
{{Â request.current_page.<your_model_class_name_in_lowercase>.<field_name> }}
For example, I extended the page model with this model:
from django.db import models
from cms.extensions import PageExtension
from cms.extensions.extension_pool import extension_pool
class ShowDefaultHeaderExtension(PageExtension):
show_header = models.BooleanField(default=True)
extension_pool.register(ShowDefaultHeaderExtension)
To access its values in the template:
{{Â request.current_page.showdefaultheaderextension.show_header }}
Since I dont have enough reputation I cannot comment on Timmy O'Mahony's Post directly. However I want to note that the proposed solution of adding a StackedInline Object to the PageAdmin.inlines list does not work any more as supposed.
I'm working with Djangocms 3.3 and somewhere between Timmy O'Mahony's version any mine the authors changed the semantic of the inline List. It's content is now shown in the Permissions Menu for that specific page (including possibly added futher StackedInline or TabularInline items).