I have a Contract class where contract_mod allows to extend a contract from a previous one. contract_mod should only show contracts related with the person that we selected previously.
The Contract class returns the field person. As I've never work with AJAX/jQuery I don't know where to start.
So, my goal is that the field contract_mod depends on hte field person (using the Admin interface).
class Contract(models.Model):
person = models.ForeignKey(Person) #person hired
contract_mod = models.OneToOneField('self', blank = True, null = True) #allows to extend a contract
...
As the field contract_mod is OneToOneField I can't use django-smart-selects or django-ajax-select
In a similar situation I did the following (now adapted to your model):
models
class Person(models.Model):
name = models.CharField(max_length=20)
def __unicode__(self):
return self.name
def get_name(self):
return self.name
class Contract(models.Model):
person = models.ForeignKey(Person) #person hired
contract_mod = models.OneToOneField('self', blank = True, null = True)
contract_name = models.CharField(max_length=20) #just for testing
def __unicode__(self):
return self.get_name() + " " +self.contract_name
def get_name(self):
return self.person.get_name() #to make sure you get the person name in the admin
def contract_mod_name(self):
if self.contract_mod:
return self.contract_mod.contract_name
else:
return ""
admin
class SelectField(forms.ChoiceField):
def clean(self, value):
return value
class CForm(forms.ModelForm):
contracts_from_selected = SelectField()
class Meta:
model = Contract
widgets = { 'contract_mod' : forms.widgets.Select(attrs={'hidden' : 'true'}) }
class CAdmin(admin.ModelAdmin):
form = CForm
list_display = ('contract_name','get_name','contract_mod_name')#what you like
def save_model(self, request, obj, form, change):
if request.POST.get('contracts_from_selected'):
obj.contract_mod=Contract.objects.get(id=int(request.POST.get('contracts_from_selected')))
obj.save()
Override the change_form.html template (by copying it from the django/contrib/admin/templates/admin into your yourapp/templates/admin/yourapp directory) and add the following Javascript:
$(function () {
$("#id_person").change(function () {
var options = $("#id_contract_mod option").filter(function () {
return $(this).html().split(" ")[0] === $("#id_person option:selected").html();
}).clone();
$("#id_contracts_from_selected").empty();
$("#id_contracts_from_selected").append(options);
});
});
One shortcoming is, that this uses the visible html entry to store the person - contract relation. So the person is also visible in the dropdown. To avoid this you could add an attribute to the options instead - see here:
Django form field choices, adding an attribute
Yes and it would be good to completely hide the contract_mod ChoiceField. Via the hidden=True in the widget only the dropdown is hidden.
Related
I would like to use tom-select as an Django autocomplete select-widget.
In the corresponding database table (Runners) are several hundred rows, so that I can't load all into the html page.
In the model RaceTeam I have a ForeignKey to Runner.
The select widget will be used in whole pages and in html fragments (via htmx).
I tried subclass django.forms.Select, but this sucks all db rows into widget.choices.
I use this solution:
I copy the tom-select JS+CSS to static/relayrace/vendor/.
class RunnerSelectWidget(Select):
class Media:
css = {
'all': ('relayrace/vendor/tom-select.css',)
}
js = ('relayrace/vendor/tom-select.complete.min.js',)
def set_choices(self, value):
# there can be several thousand choices. We don't want them here
self._choices = []
def get_choices(self):
choices = list(self._choices)
return choices
choices = property(get_choices, set_choices)
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
context['widget']['attrs']['class']='runner-select'
return context
def optgroups(self, name, value, attrs=None):
# Make the initial value available
self._choices = [(id, runner_to_select_text(Runner.objects.get(pk=id))) for id in value]
return super().optgroups(name, value, attrs)
def runner_select_json(request):
# You might want to add some permission checking here. Otherwise everybody
# can get data via this endpoint.
query = request.GET.get('q', '').strip()
qs = get_queryset(query)
items = [dict(value=r.pk, text=runner_to_select_text(r)) for r in qs]
return JsonResponse(dict(items=items, total_count=len(items)))
def runner_to_select_text(runner):
return f'{runner.user.first_name} {runner.user.last_name}'
def get_queryset(query):
if not query:
return Runner.objects.none()
return Runner.objects.filter(Q(user__username__icontains=query)|
Q(user__first_name__icontains=query)|
Q(user__last_name__icontains=query)).order_by('user__username')
Usage of the widget:
class RaceTeamForm(forms.ModelForm):
class Meta:
model = RaceTeam
widgets = {
'leader': RunnerSelectWidget(),
}
JS to init the widget on whole page load and on fragment load:
htmx.onLoad(function(elt) {
var allSelects = htmx.findAll(elt, ".runner-select")
for( select of allSelects ) {
new TomSelect(select, {
// fetch remote data
load: function(query, callback) {
var url = '/runner_select_json?q=' + encodeURIComponent(query);
fetch(url)
.then(response => response.json())
.then(json => {
callback(json.items);
}).catch(()=>{
callback();
});
}
})}});
urls.py
url('runner_select_json', runner_select_json, name='runner_select_json')
So I made a django ajax that everytime there is a new message the notification object that I add to my user models will be +1, the problem is that I dont know why is giving me this error:
django.contrib.auth.models.User.DoesNotExist: User matching query does not exist.
here is my code:
views.py:
def count_notification(request):
user = request.user
obj = user.profile.objects.get(pk=1)
obj.notifications = obj.notifications +1
obj.save()
response_data = {'success':1}
return HttpResponse(json.dumps(response_data), content_type="application/json")
urls.py:
path('messages/notification/', count_notification)
html file js
// Add the notification val
$.get('notification/')
models.py:
class ProfileImage(models.Model):
"""
Profile model
"""
user = models.OneToOneField(
verbose_name=_('User'),
to=settings.AUTH_USER_MODEL,
related_name='profile',
on_delete=models.CASCADE
)
avatar = models.ImageField(upload_to='profile_image')
notifications = models.FloatField(default='0')
thank you for the help
obj = user.profile.objects.get(pk=1)
i think your app is dealing with many users, each user has its own profile with its own notification count, so why you are fixing the id / pk to 1, you need to get the related profile, not hard code the id
try this code below
def count_notification(request):
user = request.user
user.profile.notifications += 1
user.profile.save()
response_data = {'success':1}
return HttpResponse(json.dumps(response_data), content_type="application/json")
and change
notifications = models.FloatField(default='0')
to
notifications = models.IntegerField(default='0')
it does make more sens since notifications count is an integer not float nand don't forget to rerun migrations
Update
i guess you have not defined a custom User model in settings.py in this case you need to make some changes in models.py
refer to https://docs.djangoproject.com/en/3.0/topics/auth/customizing/#reusable-apps-and-auth-user-model
from django.contrib.auth import get_user_model # get the default User model
User = get_user_model()
class ProfileImage(models.Model):
"""
Profile model
"""
user = models.OneToOneField(
verbose_name=_('User'),
# to=settings.AUTH_USER_MODEL, # you don't need this since you didn't
# defined a custom User model in
# setting.py
to = User, # HERE
related_name='profile',
on_delete=models.CASCADE
)
avatar = models.ImageField(upload_to='profile_image')
notifications = models.IntegerField(default='0') # change the field type
Update 2
from django.contrib.auth import get_user_model
User = get_user_model()
..
def count_notification(request):
# user = request.user
user = User.objects.filter(id=request.user.id).first() # HERE
user.profile.notifications += 1
user.profile.save()
response_data = {'success':1}
return HttpResponse(json.dumps(response_data), content_type="application/json")
I am trying to use django rest framework to create instances of a Bookmark model for different types of content, for example Book.
The code:
models.py
class Bookmark(models.Model):
user = models.ForeignKey(User)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
api.py
from django.contrib.contenttypes.models import ContentType
from rest_framework import viewsets, mixins, serializers, permissions
from .models import Bookmark
class BookmarkSerializer(serializers.ModelSerializer):
class Meta:
model = Bookmark
fields = ('id', 'user', 'content_type', 'object_id')
class BookmarkViewSet(mixins.CreateModelMixin,
mixins.ListModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
viewsets.GenericViewSet):
queryset = Bookmark.objects.all()
serializer_class = BookmarkSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def perform_create(self, serializer):
content_type = self.request.query_params.get('content_type')
app, model = content_type.split(".")
ct = ContentType.objects.get_by_natural_key(app, model)
object_id = self.request.query_params.get('object_id')
serializer.save(user=self.request.user, content_type=ct, object_id=object_id)
def get_queryset(self):
items = Bookmark.objects.filter(user=self.request.user)
content_type = self.request.query_params.get('content_type', None)
if content_type:
app, model = content_type.split(".")
ct = ContentType.objects.get_by_natural_key(app, model)
items = items.filter(content_type=ct)
object_id = self.request.query_params.get('object_id', None)
if object_id:
items = items.filter(object_id=object_id)
return items
get_queryset part is fine. But perform_create fails when I try to create a new Bookmark:
var item = new Bookmark({'content_type': 'books.book', 'object_id': self.book_id});
item.save();
The response:
{"user":["This field is required."],"content_type":["Incorrect type. Expected pk value, received unicode."]}
It is not clear to me how I am supposed to do this. I would appreciate any feedback.
You should post your model definition as well, but the error you are getting is saying that you also need to pass a user argument to Bookmark and that you should be passing a pk value (aka an integer that is the id of the object you're pointing to) for content_type.
I can't say anything about the error message concerning the user, but here is my 2 cents about the second one.
You do not simply give a path to your model as a string to the content_type key.
Apparently
, you have to use the get_for_model method which is passed as parameter a model (The model 'Book', not an instance of it), and returns an object of type ContentType. This object's id is what you should pass as a value.
Both previous answers pointed me in the right direction. In the end, the solution was not to override perform_create, but create function:
def create(self, request, *args, **kwargs):
content_type = request.data['content_type']
if content_type:
app, model = content_type.split(".")
ct = ContentType.objects.get_by_natural_key(app, model)
request.data['user'] = request.user.id
request.data['content_type'] = ct.id
#object_id already passed as id in request.data
A further explanation can be read here:
https://github.com/encode/django-rest-framework/issues/3470
how can I secure SaveChanges after I added or deleted an entity to breezejs?
var newTodo = todoType.createEntity(initialValues);
manager.addEntity(newTodo);
I want only to add/delete entities to a logged in user. Other users shouldn't be able to add an entity to another user via javascript hack.
Querying only allowed entites is possible via editing EFContextProvider on the server. But how does it work with delete or add?
You can Prevent save Change on server side using
overriding BeforeSaveEntitiesDelegate method of contexProvider.
E.g
_contextProvider.BeforeSaveEntitiesDelegate = BeforeSaveEntities;
private Dictionary<Type, List<EntityInfo>> BeforeSaveEntities(Dictionary<Type, List<EntityInfo>> arg)
{
var resultToReturn = new Dictionary<Type, List<EntityInfo>>();
foreach (var type in arg.Keys)
{
var entityName = type.FullName;
var list = arg[type];
if (entityName == "xyz" && list[0].EntityState!="Added")
{
resultToReturn.Add(type, list);
}
}
return arg;
}
This will not save new added entity name "xyz".
I am having a hard time getting access to the rawCorpus variable that I return in my views. When ever I test it with an alert: alert({{ rawCorpus }}) it pops up with undefined as the message. I really need a way to test things as I go along in views. IE: a print method for my individual variables that doesn't have to go through the return process.
views.py
def homesite(request):
rawCorpus = ""
corpusitems = CorpusItem.objects.order_by('name')
if (request.method == 'POST') and "namefortextinput" in request.POST:
f = CorpusItemForm(request.POST)
if f.is_valid():
new_corpusitem = f.save()
elif (request.method == 'POST') and 'selectedCorpusChoiceTokenizeWords' in request.POST:
g = CorpusChoiceForm(request.POST)
if g.is_valid():
new_corpuschoice = g.save()
rawCorpus = g.__getitem__(name)
Models.py
class CorpusItem(models.Model):
name = models.CharField(max_length=100)
content = models.CharField(max_length=9000)
def __unicode__(self):
return self.name
class CorpusItemForm(ModelForm):
class Meta:
model = CorpusItem
def __str__():
return name
from django import forms
class CorpusChoice(models.Model):
name = models.CharField(max_length=100)
class CorpusChoiceForm(ModelForm):
class Meta:
model = CorpusChoice