Different validation for different actions - ruby-on-rails-4

Here's the situation. I have a BlogPost model wich has a title, content, category_name, published_at date, some other fields and some presence validations.
What I want to do is to let the user save a post as a "draft" (withought publishing it). In that case I need to skip most of the validations (pretty much all of them, except for the title and category_name). What is the best Rails 4 way to do that?
Being more precise:
Should I leave it one model, or create another model like DraftPost and inherit BlogPost from it, adding validations?
Or should I leave it in one model and implement the validations in the controller (what I can think of is validating the title and category_name manualy on the draft save and saving the model with save!)
Or some other way?

Ended up adding a draft field to the model and validationg with :if => :draft? Seems it's the best way to work with it.

Related

Is there any possibility to add fields inside the fields of a Model

Assume I have a model named MyModel and I have a Field Named field Now I want to add three more fields inside the prescription like one field_a , field_b and field_c .
Does Django Allow that in any way or we have to make another model for this purpose and then link with Foreign Key to MyModel?
Well I think it is an idea that could lead to some really hard to maintain code and should be well thought through.
That aside If you have a look at:
https://docs.wagtail.io/en/stable/topics/streamfield.html
It's a special field meant for cms applications that uses json in the field to model dynamic fields and validations for it.
Also if you run postgres this might help. https://docs.djangoproject.com/en/3.2/ref/contrib/postgres/fields/#jsonfield

two-stage submission of django forms

I have a django model that looks something like this:
class MyModel(models.Model):
a = models.BooleanField(default=False)
b = models.CharField(max_length=33, blank=False)
c = models.CharField(max_length=40, blank=True)
and a corresponding form
class MyForm(ModelForm):
class Meta:
model = MyModel
Asking the user to fill in the form is a two phase process. First I ask whether a is True or False. After a submit (would be better maybe with ajax, but keep it simple at first) I can fill in a dropdown list with choices for b and decide whether or not to show c as an option. No need to show a as a choice any more, it's descriptive text.
So I want to present a form twice, and the same model is behind it, slowly being filled in. First choose a, then be reminded that a is True and be asked about b and c. Or that a is False and be asked about only b. After submitting this second form, I save the object.
I'm not clear how best to do this in django. One way is to make two separate form classes, one of which has hidden fields. But if the same model is behind both and the model has required fields, I'm anticipating this will get me in trouble, since the first form won't have satisified the requirement that b be non-empty. In addition, there's a small amount of fragility introduced, since updating the model requires updating two forms (and a probably at least one view).
Alternatively, I could use non-model forms and have full freedom, but I'd love to believe that django has foreseen this need and I can do it easier.
Any suggestions on what the right idiom is?
You can use Form Wizard from form-tools for that: https://django-formtools.readthedocs.io/en/latest/wizard.html
It works rather simple by defining multiple forms and combining them. Then in the end, you can use the data to your liking with a custom done() form. The docs tell you everything. You can use JS to hide some of your fields for the super quick approach (utilize localStorage for example).

How to query the database in Django forms?

I have a model:
class Registration(models.Model):
student_name = models.CharField(max_length=50)
season = models.ForeignKey(Season, on_delete=models.CASCADE)
subject = models.ForeignKey(Subject, on_delete=models.CASCADE)
address = models.TextField()
class Meta:
unique_together = (('student_name', 'selected_season', 'selected_subject'),)
I want to create a form for this model where the user will be given the option to select the subject based on the season that they selected. I have models for them as well:
class Subject(models.Model):
subject_name = models.CharField(max_length=50)
...
class Season(models.Model):
season = models.CharField(max_length=2, primary_key=True)
subject = models.ManyToManyField(Subject)
...
I dont know how to query the Form. Should It be a ModelForm or a regular Form? How to query the database in the form?
You can only know which season was selected when the form is submitted, so there's no simple direct way to implement this (note that this is a HTTP limitation, not a django one). IOW you'll need either a "wizard" process or front-end scripting.
The "wizard" solution is: one first form where the user selects the season, user submits the form, your code selects the related subjects and displays a second form with subjects choices, user selects subjects and submits for final validation (nb: this is usually done within a single view, using a form hidden field to keep track of the current step and which season was selected in first step). This is garanteed to work (if correctly implemented of course xD), but not really user friendly.
Second solution is to use front-end scripting.
In it's simplest form, when the user selects the season, you use js to hide other seasons subjects (or you first hide all subjects and only display relevant ones when the season is selected). This can be done rather simply by grouping all subjects for a given season in a same fieldset (or whatever other container tag) with an id matching the season's one, or by having a distinct html "select" (with same name but different ids) per season. Of course you can also have all subjects in one single html select (or whatever), keep a front-side (js) mapping of seasons=>subjects, and update your select or whatever from this mapping.
This solution can be made to work (in "degraded" mode) without JS (you just have to make sure the subjects selector(s) are visible and active by default). You'll have to implement a custom validation backend-side (cf django's forms doc for this) to make sure the subject matches the season anyway (never trust front-side validation, it's only user-friendly sugar), so in the worst case the form will just not validate.
Now if you have a LOT of seasons and subjects, you may want to prefer doing an ajax query when the user selects the season and populate the subjects selector from this query's result (to avoid initially rendering a huge list of options), but then you can't make it work without JS anymore (whether this is an issue or not depends on your requirements).
EDIT
If i do follow the form wizard option, I need to create 2 forms, the first consisting of just the Season.
Well, you could do it with one single form (passing an argument to specify what should be done) but using two forms is much simpler indeed.
The 2nd form will consist of the rest of the options (except for seasons), am I right? So should the 2nd form be a modelform?
Well, that's the point of modelforms, isn't it ?
How do I put a queryset in the ModelChoiceField in modelform? I googled but could't find anything –
In your form's __init__, you update self.fields["subject"].queryset with your filtered queryset.
IMPORTANT: do NOT try to touch self.fieldname - this is the class-level field definition, so changing it would totally unexpected results in production (been here, done that, and that was quite some fun to debug xD).
Now this being said, for your use case, I reckon you'd be better with the simple js filtering solution - it's the easiest to set up - on the backend it's totally transparent, and the front-end part is rather simple using jQuery -, it's much more user-friendly, and it's garanteed to work.

Django way for dynamic forms with dependencies?

I'm looking for a django way to handle some complex forms with a lot of business logic. The issue is many of my forms have dependencies in them.
Some examples:
1. two "select" (choice) fields that are dependent on each other. For example consider two dropdowns one for Country and one for City.
2. A "required-if" rule, i.e set field required if something else in the form was selected. Say if the user select "Other" option in a select field, he need to add an explanation in a textarea.
3. Some way to handle date/datetime fields, i.e rules like max/min date?
What I'm doing now is implementing all of these in the form clean(), __init__(), and write some (tedious) client-side JS.
I wonder if there is a better approach? like defining these rules in a something similar to django Meta classes.
I'm going to necro this thread, because I don't see a good answer yet. If you are trying to validate a field and you want that field's validation to depend on another field in that same form, use the clean(self) method.
Here's an example: Say you have two fields, a "main_image" and "image_2". You want to make sure that if a user uploads a second image, that they also uploaded a main image as well. If they don't upload an image, the default image will be called 'default_ad.jpg'.
In forms.py:
class AdForm(forms.ModelForm):
class Meta:
model = Ad
fields = [
'title',
'main_image',
'image_2',
'item_or_model_names',
'category',
'buying_or_selling',
'condition',
'asking_price',
'location',
]
def clean(self):
# "Call the cleaned form"
cleaned_data = super().clean()
main_image = cleaned_data.get("main_image")
image_2 = cleaned_data.get("image_2")
if "default_ad" not in image_2:
# Check to see if image_2's name contains "default_ad"
if "default_ad" in main_image:
raise forms.ValidationError(
"Oops, you didn't upload a main image."
)
If you want more info, read: https://docs.djangoproject.com/en/2.2/ref/forms/validation/#cleaning-and-validating-fields-that-depend-on-each-other
Good luck!
1.This task is souly related the the html building of the form, not involving django/jinga.
2.Here, you go to dynamic forms. the best and most used way nowdays to do this, is through JS.
3.try building a DB with a "time" type field and then through "admin" watch how they handle it. all of special fields useage is covered here: https://docs.djangoproject.com/en/1.9/ref/forms/fields/

Django Model Field Metadata

I have a Django model and I wish to denote that some of the model fields are private so that when I display a ModelForm based on this model I can display these fields marked as such.
I'd like this to be specified on the model rather than the form as I think that's where it belongs.
I'm wondering what the best way to do this is. Can I write a decorator #private to do this? Can anyone point me at an example?
Thanks
There's no one feature that perfectly fits your problem, but here's a couple of suggestions:
Add the information to the model's Meta class, which you can then access from your form via the _meta attribute on the model
Utilise the help_text option on your model fields (obviously this won't help you to "decide programatically which fields I should be displaying to other users")
I do agree that it's debatable as to whether this belongs at the model layer; it seems like business logic to me.