Django bulk edit form - django

In my project I have a many-to-one relation with one Serie having up to 20 samples. I'm trying to implement a system where a user uploads a (csv)file and is then sent to a page where they can fill in any missing data. I haven't found a way to implement this and was looking for some help. The idea is that I want a table with each row having 4 fields, which are linked to the sample in question and the table will have as many rows as there were samples in the (csv)file. Furthermore, if data is already present for one of the fields, I want it to prefill the field in question. In the end, the updated data is committed by pressing a single save button at the bottom so it's supposed to be one big form.
I've added the models below. The attributes which I want to update in the form are index, pool and remarks, which may be present in the csv file already but often they aren't.
Can anyone give me any tips on how to implement this or hint at methods which are best for implementing something like this?
Models
class Sample(models.Model):
name = models.CharField(max_length=20)
pool = models.ForeignKey(Pool, blank=True, null=True)
serie = models.ForeignKey(Serie, blank=True, null = True)
index = models.ForeignKey(Index, blank=True, null=True)
remarks = models.CharField(max_length=50, blank=True)
def __str__(self):
return self.name
class Serie(models.Model):
a_type = models.ForeignKey(Atype)
name = models.IntegerField()
def __str__(self):
return str(self.a_type)+'-'+str(self.name)
Views
def serie_edit(request,serie_id):
try:
serie = Serie.objects.get(pk=serie_id)
except Serie.DoesNotExist:
raise Http404
index_list = Index.objects.all()
sample_list = Sample.objects.filter(serie__id = serie_id)
return render(request, 'samples/serie_edit.html', {'serie':serie, 'sample_list':sample_list, 'index_list':index_list})

Related

how to build query with several manyTomany relationships - Django

I really don't understand all the ways to build the right query.
I have the following models in the code i'm working on. I can't change models.
models/FollowUp:
class FollowUp(BaseModel):
name = models.CharField(max_length=256)
questions = models.ManyToManyField(Question, blank=True, )
models/Survey:
class Survey(BaseModel):
name = models.CharField(max_length=256)
followup = models.ManyToManyField(
FollowUp, blank=True, help_text='questionnaires')
user = models.ManyToManyField(User, blank=True, through='SurveyStatus')
models/SurveyStatus:
class SurveyStatus(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
survey = models.ForeignKey(Survey, on_delete=models.CASCADE)
survey_status = models.CharField(max_length=10,
blank=True,
null=True,
choices=STATUS_SURVEY_CHOICES,
)
models/UserSurvey:
class UserSurvey(BaseModel):
user = models.ForeignKey(User, null=True, blank=True,
on_delete=models.DO_NOTHING)
followups = models.ManyToManyField(FollowUp, blank=True)
surveys = models.ManyToManyField(Survey, blank=True)
questions = models.ManyToManyField(Question, blank=True)
#classmethod
def create(cls, user_id):
user = User.objects.filter(pk=user_id).first()
cu_quest = cls(user=user)
cu_quest.save()
cu_quest._get_all_active_surveys
cu_quest._get_all_followups()
cu_quest._get_all_questions()
return cu_quest
def _get_all_questions(self):
[[self.questions.add(ques) for ques in qstnr.questions.all()]
for qstnr in self.followups.all()]
return
def _get_all_followups(self):
queryset = FollowUp.objects.filter(survey__user=self.user).filter(survey__user__surveystatus_survey_status='active')
# queryset = self._get_all_active_surveys()
[self.followups.add(quest) for quest in queryset]
return
#property
def _get_all_active_surveys(self):
queryset = Survey.objects.filter(user=self.user,
surveystatus__survey_status='active')
[self.surveys.add(quest) for quest in queryset]
return
Now my questions:
my view sends to the create of the UserSurvey model in order to create a questionary.
I need to get all the questions of the followup of the surveys with a survey_status = 'active' for the user (the one who clicks on a button)...
I tried several things:
I wrote the _get_all_active_surveys() function and there I get all the surveys that are with a survey_status = 'active' and then the _get_all_followups() function needs to call it to use the result to build its own one. I have an issue telling me that
a list is not a callable object.
I tried to write directly the right query in _get_all_followups() with
queryset = FollowUp.objects.filter(survey__user=self.user).filter(survey__user__surveystatus_survey_status='active')
but I don't succeed to manage all the M2M relationships. I wrote the query above but issue also
Related Field got invalid lookup: surveystatus_survey_status
i read that a related_name can help to build reverse query but i don't understand why?
it's the first time i see return empty and what it needs to return above. Why this notation?
If you have clear explanations (more than the doc) I will very appreciate.
thanks
Quite a few things to answer here, I've put them into a list:
Your _get_all_active_surveys has the #property decorator but neither of the other two methods do? It isn't actually a property so I would remove it.
You are using a list comprehension to add your queryset objects to the m2m field, this is unnecessary as you don't actually want a list object and can be rewritten as e.g. self.surveys.add(*queryset)
You can comma-separate filter expressions as .filter(expression1, expression2) rather than .filter(expression1).filter(expression2).
You are missing an underscore in surveystatus_survey_status it should be surveystatus__survey_status.
Related name is just another way of reverse-accessing relationships, it doesn't actually change how the relationship exists - by default Django will do something like ModelA.modelb_set.all() - you can do reverse_name="my_model_bs" and then ModelA.my_model_bs.all()

Django one to many models - How to create the "many" when the "one" is created?

There are a lot of questions along these lines, but so far as I can tell, but many are quite old and I haven't found one that helped me understand my use case. I think I want to be looking at signals, but I'm not clear on exactly what I should be doing in terms of Django patterns and best practices.
Here's my Model code:
from django.db import models
from django.db.models.fields import SlugField, TextField
from django.utils import timezone
# Hexes have Terrains, which impart intrinsic properties, like base speed and .png image
# They're NOT 1:1 with Hexes IE) You might want Slow and Normal Grasslands with the same image
class Terrain(models.Model):
FAST = 'F'
NORMAL = 'N'
SLOW = 'S'
SPEED_CHOICES = [
(FAST, 'Fast'),
(NORMAL, 'Normal'),
(SLOW, 'Slow'),
]
name = models.CharField(max_length=64)
speed = models.CharField(max_length=6, choices=SPEED_CHOICES, default=NORMAL)
image = models.ImageField(upload_to='images/', blank=True)
def __str__(self):
return self.name
# Grids establish the dimensions of a HexMap. Grids may be used in many HexMaps
class Grid(models.Model):
name = models.CharField(max_length=64)
size = models.IntegerField(default=72)
num_rows = models.IntegerField(default=10)
num_cols= models.IntegerField(default=10)
def __str__(self):
return self.name
# Hexes represent the "tiles" on a Grid. A single Hex may appear in many Grids
class Hex(models.Model):
name = models.CharField(max_length=64, blank=True, null=True)
terrain = models.ForeignKey(Terrain, on_delete=models.CASCADE)
grids = models.ManyToManyField(Grid)
def __str__(self):
return self.id
class Meta:
verbose_name_plural = "Hexes"
# Locations are coordinal points on a HexMap. They may contain many Annotations
class Location(models.Model):
# HexMap Name + row + col, for a consistent way to ID locations
name = models.CharField(max_length=64, blank=True, null=True)
friendly_name = models.CharField(max_length=64, blank=True, null=False)
# TODO constrain these to the sizes specified in the grids? Sane defaults?
row = models.PositiveIntegerField()
col = models.PositiveIntegerField()
# Authors create Annotations
# TODO create Players and make Authors extend it so we can do rights and perms
class Author(models.Model):
first_name = models.CharField(max_length=64, blank=False, null=False)
last_name = models.CharField(max_length=64, blank=False, null=False)
join_date = timezone.now()
def __str__(self):
return self.first_name + " " + self.last_name
# Annotations are entries created by Authors. A location may have many Annotations
class Annotation(models.Model):
name = models.CharField(max_length=64, blank=False, null=False)
pubdate = join_date = timezone.now()
content = TextField()
# Hexmaps put it all together and allow fetching of Locations by (r,c) coords
class HexMap(models.Model):
name = models.CharField(max_length=64, blank=True, null=True)
rows = {}
cols = {}
#TODO - how do I create grid.num_rows * grid.num_cols locations and put them into the dicts?
I am probably making a ton of mistakes as I've not worked in Django in close to a decade. Any constructive feedback is appreciated in the comments. Most of the relationships are not coded in, as I'm not sure how they should look.
My specific question, however, is related the HexMap and Location objects.
When a HexMap is created, I want to create a set of empty Locations with a default name value matching a naming convention like HexMap.name + row + col and fill the row{} and col{} dicts of the new HexMap with them.
My instinct is that a Hexmap needs a Grid in order to be created, so I could create a nested loop inside HexMap with grid.num_rows as as the outer loop and num_cols as the inner loop. Each iteration through the inner loop builds a row of Locations as a dict. Each iteration of the outer loop adds that row to the rows{} dict. One thing I'm not certain about is how I'd get the grid.num_rows and grid.num_cols when the HexMap isn't even created yet, and doesn't have a grid associated.
Is this anywhere near the right way to do this in Django, or am I off the mark? Many of the answers and online tutorials seem to indicate that I need to look at signals, but I'm just not really clear on whether or not that's the case. Also, if so, I could use some similar examples to review. The Django docs are great and helpful, but when you're "new" to it all, it can be hard to parse and adopt the examples.
Found this question, which is the question I really should have asked in the first place. While a few years old, I think it's still the right answer and helped direct me to the appropriate documentation. Still not sure what my next step is, thinking I need some sane defaults and an override of the save method to use those if the instances of the other fields don't exist yet. I'm still working out wether or not I have a valid use case for signals here. I think that I will eventually when I add more apps to the project and actions in those apps need to interact with the models in this app.

Django: Run a function if certain condition is met

I have a Word model, where a user can add words and various fields, like this (shortened version):
class Word(models.Model):
target_word = models.CharField()
source_word = models.CharField()
add_to_review = models.BooleanField(default=True)
example_sentence = models.CharField()
image = models.ImageField(upload_to='images/',blank=True)
audio = models.FileField(upload_to='audio/',blank=True)
I also have a Flashcard model where a user can add and study flashcards. Here I show only the relevant code:
class FlashcardManager(models.Manager):
def create_flashcard(self, user, question, answer, deck_name):
try:
deck = Deck.objects.get(owner=user, name=deck_name)
except ObjectDoesNotExist:
deck = Deck(owner=user, name=deck_name)
deck.save()
self.create(owner=user, question=question, answer=answer,
deck=deck)
return deck
class Flashcard(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE)
deck = models.ForeignKey(Deck, on_delete=models.CASCADE)
question = models.TextField()
answer = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
last_shown_at = models.DateTimeField(auto_now_add=True)
next_due_date = models.DateTimeField(default=timezone.now)
difficulty = models.FloatField(default=2.5)
consec_correct_answers = models.IntegerField(default=0)
objects = FlashcardManager()
def __str__(self):
return self.question
When a user creates a new word, I want to give them the option of automatically creating a flashcard using the info provided. In other words, question = target_word, answer = source_word, and the deck name can either be some default value, or I can add a field to the Word model. Is there a way I can do this by using the add_to_review field of the Word model and the create_flashcard method of the FlashcardManager model? I've tried adding a function to my Word model that runs if add_to_review is true. I don't get any errors, but nothing happens.
I know one option would be to just combine the two models... but I do have my own reasons for keeping them seperate. I might still do that, but I was just wondering if what I am trying to do is possible / feasible? I'm not really sure what exactly to google, so haven't been able to find any info on this. So any advice would be great, thanks!
Not sure if this is the best way, but it worked. Initially I had problems with the signal not being called, but this got fixed when I moved it to my models.py file instead of having it separate in a signals.py file.
#receiver(post_save, sender=Word)
def flash_from_word(sender, created, instance, **kwargs):
if created:
flash_wanted = Word.objects.filter(add_to_review=True)
instance._meta.model.objects.all()
target_word = instance.target_word
source_word = instance.source_word
deck_name = instance.deck_name
user = instance.user
if flash_wanted:
flash = Flashcard.objects.create(owner=user, question=target_word,answer=source_word,deck=Deck.objects.get(name=deck_name,owner=user))
post_save.connect(flash_from_word, sender=Word)

Django: what is the better way to get users info from models in view or template?

I have few django models and I want display some information the for several users in the template.
Below are the models:
class CustomUser(AbstractUser):
def __str__(self):
return self.email
class Post(models.Model):
author = models.ForeignKey(CustomUser,on_delete=models.CASCADE,)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True, null=True)
post_url = models.URLField(max_length = 200, blank = True)
slug = models.SlugField(unique=True, blank=True)
class subscription(models.Model):
creator = models.ForeignKey(CustomUser,default=None, null=True,on_delete=models.CASCADE,related_name='creator',)
booster = models.ForeignKey(CustomUser,default=None, null=True,on_delete=models.CASCADE,related_name='booster')
sub_value = models.FloatField(blank = True)
sub_id = models.TextField(blank = True)
status = models.BooleanField(default=False)
dateSubscribed = models.DateTimeField(default=timezone.now)
dateSubscriptionEnded = models.DateTimeField(default=timezone.now)
paymentCount = models.FloatField(default= 0)
I want to pass few users to template and display how many posts and subscriptions each user has? I am wondering what is the best way to do it? Is better number of posts and subscribers information in the view and just pass those things to template or pass users get that information in the template? Thanks!
Model => View => Template
Try to parse as much of the information from the model in the view as possible. The reason for this is the pure python in the view runs fast and is nicer to work with the pure python. So when I can I try to break down information in the view into lists of objects. So for your example.
determine what users you want and add them to a list then loop through the list filtering using the username or id.
ex:
Post.objects.filter(author='User')
then create a list of objects with the relevant user, post count, and sub info.
then pass this to the template you can loop through the list using all the relevant data in your objects.
hope that was clear and useful some of that is my own development bias there may be a better way but that's how I have approached a similar issue in the past. good luck!

django cms plugin that display model with specific value checked

I made a model that displays articles and when you create an article you have the possibility to choose if this article will be a featured one.
So this is basically what I have in my Article model :
class Article(ModelMeta, TranslatableModel):
taints_cache = True
"""
Press article element,
"""
date_created = models.DateTimeField(auto_now_add=True)
date_modified = models.DateTimeField(auto_now=True)
date_realization = models.DateField(_('Realised in'),
default=timezone.now)
image = FilerImageField(verbose_name=_('Featured image'), blank=True,
null=True,
on_delete=models.SET_NULL,
related_name='image_press_article',
help_text=_('Set if the article will be featured'))
sources = models.ManyToManyField(ArticleSource, verbose_name=_('Source'),
blank=False, null=True, related_name='sources_press_article')
regions = models.ManyToManyField(Country, verbose_name=_('Country of the article'),
blank=True, null=True,
related_name='regions_press_article')
global_regions = models.BooleanField('Global', default=True)
featureArticle = models.BooleanField(_('Feature'), help_text=_('Feature this article'), default=False)
Then, I created a plugin that displays the featured articles.
But the thing is, in the django plugin admin I let the user the possibility to choose which article he wants to display (with a maximum of 3).
But in this choosing list, all my articles are listed.
What I want to, is to list only the articles that are checked as "featured", in my plugin admin. Instead of having all the articles.
Here what I have with my cms_plugin's model :
class FeaturedArticlePlugin(CMSPlugin):
selected_article = SortedManyToManyField(Article, blank=True, verbose_name=_('Selected articles'),
help_text=_('Select the featured articles to display'))
def __str__(self):
return u'%s Selected articles' % self.selected_article.all()
def copy_relations(self, oldinstance):
self.selected_article = oldinstance.selected_article.all()
And in my cms_plugins.py :
class PressPlugin(CMSPluginBase):
module = 'Press'
class PressFeaturedArticlePlugin(PressPlugin):
module = _('Press')
name = _('Press feature')
model = FeaturedArticlePlugin
render_template = 'djangocms_press/plugins/feature_article.html'
number_article = 3
def render(self, context, instance, placeholder):
"""
Get a list of selected_articles
"""
selected_article = instance.selected_article.all()
number_selected_article = selected_article.count()
feature_article_list = list(selected_article[:self.number_article])
context['instance'] = instance
context['feature_article_list'] = feature_article_list
return context
plugin_pool.register_plugin(PressFeaturedArticlePlugin)
So, I am sure it's nothing complicated but I can't point this out.
Anyone has a clue ?
EDIT
From what I understand, the only thing that concern the display of all articles is this line :
selected_article = SortedManyToManyField(Article, blank=True, verbose_name=_('Selected articles'),
help_text=_('Select the featured articles to display'))
So what I am suppose to do is to filter this selected_article with the featureArticle=True. But how to do it ?
I am not quite sure if I am missing something, but, couldn't you just apply a filter here?
selected_article = instance.selected_article.all().filter(featureArticle=true)
number_selected_article = selected_article.count()
Or is the problem with the lines after?
feature_article_list = list(selected_article[:self.number_article])
If your problem is selecting the extra articles, maybe you need to order them by date and select only the necessary?
feature_article_list = list(Articles.all().order_by('-created')[:self.number_article - number_selected_article]
Which will only select the extra necessaries?
Edit: Your situation kind of reminds me of a problem I once had. So I'll refer you to the same page that helped me in the past just in case you'd manage to figure it out.
Restrict django admin change permissions
Edit 2 : "I created a plugin that displays the featured articles. But the thing is, in the django plugin admin I let the user the possibility to choose which article he wants to display (with a maximum of 3). But in this choosing list, all my articles are listed."
Isn't it ok if all the articles are displayed there? How can you choose among them if they are not all displayed?