How can I use Linkify with Django_tables2 - django

I have read a few similar questions and all the docs I can find on this but I am still not able to understand the solution. I have built my model to have a player, the text description of a keyboard and a url to it. I want to show in a table the player, the keyboard and i want to have the keyboard text have the url column embedded within it. Any help would be appreciated.
Models.py:
class player_hardware(models.Model):
Player = models.TextField(blank = False, primary_key = True)
keyboard = models.TextField(blank = True, max_length = 200)
keyboard_url = models.TextField(blank = True, max_length = 200)
Views.py:
class PlayerListView(SingleTableView):
model = player_hardware
table_class = PersonTable
template_name = 'application/playerlist.html'
Tables.py
class PersonTable(tables.Table):
class Meta:
model = player_hardware
template_name = "django_tables2/bootstrap4.html"
fields = ("Player", "keyboard")

If the keyboard_url is to an external website, it's simpler to add the url link in a custom render_field method on the Table.
class PersonTable(tables.Table):
def render_keyboard(self, record):
return mark_safe(f'{record.keyboard}')
The linkify field appears to be more for internal urls, but if you absolutely wanted to use linkify to create an external link, it would look like:
class PersonTable(tables.Table):
keyboard = tables.Column(linkify=self.get_keyboard_url)
def get_keyboard_url(self, record):
return record.keyboard_url

Related

Adding User model to the StructBlock class

I'm trying to create a simple blog page with image,content,date posted and user who posted.
But I don't think that wagtail allows me to put in the user model into my block.
class HomePage(Page):
template_name = 'home/home_page.html'
max_count = 1
subtitle = models.CharField(max_length=200)
content = StreamField(
[
("title_and_text_block", AnnouncementBlock())
]
)
content_panels = Page.content_panels + [
FieldPanel("subtitle"),
StreamFieldPanel("content")
]
def get_context(self, request, *args, **kwargs):
context = super().get_context(request, args, kwargs)
context['posts'] = HomePage.objects.live().public()
return context;
from wagtail.core import blocks
from wagtail.images.blocks import ImageChooserBlock
class AnnouncementBlock(blocks.StructBlock):
title = blocks.CharBlock(required=True, help_text='Add your title')
content = blocks.RichTextBlock(required=True, features=["bold", "italic", "link"])
image_thumbnail = ImageChooserBlock(required=False, help_text='Announcement Thumbnail')
class Meta:
template = "streams/title_and_text_block.html"
icon = "edit"
label = "Announcement"
My goal is everytime user posted a new announcement his/her name should appear. not sure how i can do that since in the block i cannot add the User model so that the user's detail will be saved along with the content/image etc.
something like this.
from wagtail.core import blocks
from wagtail.images.blocks import ImageChooserBlock
from django.conf import settings
class AnnouncementBlock(blocks.StructBlock):
title = blocks.CharBlock(required=True, help_text='Add your title')
content = blocks.RichTextBlock(required=True, features=["bold", "italic", "link"])
image_thumbnail = ImageChooserBlock(required=False, help_text='Announcement Thumbnail')
#USER is not allowed here. error on the model
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
class Meta:
template = "streams/title_and_text_block.html"
icon = "edit"
label = "Announcement"
Please help thanks
I think you can't use a ForeignKey inside a Streamfield Block class.
If you're OK with displaying a simple user select widget, try to subclass a ChooserBlock (see here).
If you need to assign the logged in user automatically instead, you might be able to write your own block type but 1- it's more complicated as you will have to figure out how Streamfields work internally and 2- if I remember correctly, you can't access the request object from inside a block definition.

Doing a Multi-step form with Django Class-based CreateView

I have multiple class-based createviews. My goal is to link all createviews such that when I post the first createview, it will redirect me to the second createview where it will retrieve the data entered from first createview.
Does anyone know the solution to this?
The first createview (Step01) contains django-inline-formset-factory code which is similar to this code, while the rest (Step02 and Step03) contain basic code.
I have referred to this link, but the solution is based on using function-based view. Have also made attempt using Django's Form-Wizard, but handling django-inline-formset with it is still too complicated for me.
Why do I do this? So that each group level (i.e. general, staff, managerial) can access their respective view but not the view for other group level.
This is my current code:
models.py
class Model_Step01_Cart(models.Model):
cart_name = models.CharField(max_length=100)
class Model_Step01_CartItem(models.Model):
cart = models.ForeignKey(Profile)
item_name = models.CharField(max_length = 100)
item_quantity = models.FloatField(null = True, blank = True)
class Model_Step02_Staffnote(models.Model):
note_staff = models.TextField(max_length = 500, null = True, blank = True)
class Model_Step03_Managernote(models.Model):
note_manager = models.TextField(max_length = 500, null = True, blank = True)
forms.py
class Form_Step01_Cart(forms.ModelForm):
class Meta:
model = Model_Step01_Cart
fields = ["cart_name"]
class Form_Step01_CartItem(forms.ModelForm):
class Meta:
model = Model_Step01_CartItem
fields = ["cart", "item_name", "item_quantity"]
Formset_CartItem = forms.inlineformset_factory(
Model_Step01_Cart,
Model_Step01_CartItem,
form = Form_Step01_CartItem,
extra = 3
)
class Form_Step02(forms.ModelForm):
class Meta:
model = Model_Step02_Staffnote
fields = ["note_staff"]
class Form_Step03(forms.ModelForm):
class Meta:
model = Model_Step03_Managernote
fields = ["note_manager"]
views.py
class View_Step01(CreateView):
# contains formset_factory for "Model_Step01_Cart" and "Model_Step01_CartItem"
model = Model_Step01_Cart
fields = ["cart_name"]
# similar with the code displays here: https://medium.com/#adandan01/django-inline-formsets-example-mybook-420cc4b6225d
class View_Step02(CreateView):
# gets info from step 01, and adds staff's note
class View_Step03(CreateView):
# gets info from step 01 and 02, and adds manager's note.
Find my answer to split a single form across multiple views. You can configure the gist script to fit your requirement
For class-based-view map the success url of one view to other CreateView.as_view(model=myModel, success_url="/<path-to-view2/")

Django admin page combobox, possible?

I have a simple model in django with a field using choices. As it is now, I am forced to use one of the available choices, how can I be able to use those choices and also be able to enter text manually?
WHERE_CHOICES = (
('CHL', 'Center HL'),
('CHD', 'Center HD')
)
class Event(models.Model):
ev_start = models.DateTimeField()
ev_end = models.DateTimeField()
where = models.CharField(max_length=3, default='CHL', choices=WHERE_CHOICES)
Thanks
You can override the admin form field widget with a datalist tag.
It would look like that (using the floppyforms library):
class EventAdminForm(ModelForm):
class Meta:
model = Event
fields = "__all__"
widgets = {
'where': floppyforms.widgets.Input(datalist=[v[0] for v in Event.WHERE_CHOICES])
}
class EventAdmin(admin.ModelAdmin):
form = EventAdminForm
admin.site.register(Event, EventAdmin)

django change formfield data

I am working with django-taggit (https://github.com/alex/django-taggit). To let a user add tags i use a formfield that I convert into the tags and add them.
However, when i try to load the template for editing. i get the taggit objects in my bar.
Now i want to convert those in a normal readable string again.
However, i can't seem to edit that field of the instance before passing it to my form.
The Form:
class NewCampaignForm(forms.ModelForm):
""" Create a new campaign and add the searchtags """
queryset = Game.objects.all().order_by("name")
game = forms.ModelChoiceField(queryset=queryset, required=True)
tags = forms.CharField(required=False)
focus = forms.ChoiceField(required=False, choices=Campaign.CHOICES)
class Meta:
model = Campaign
fields = ["game", "tags", "focus"]
exclude = ["owner"]
my model:
class Campaign(models.Model):
""" campaign information """
ROLEPLAY = "Roleplay"
COMBAT = "Combat"
BOTH = "Both"
CHOICES = (
(ROLEPLAY, "Roleplay"),
(COMBAT, "Combat"),
(BOTH, "Both"),
)
owner = models.ForeignKey(User)
game = models.ForeignKey(Game)
focus = models.CharField(max_length=15, choices=CHOICES)
tags = TaggableManager()
view:
def campaign_view(request, campaign_id):
campaign = get_object_or_404(Campaign, pk=campaign_id)
campaign.tags = "Some string"
new_campaign_form = NewCampaignForm(instance=campaign)
But when i try this i still get the taggit objects([]) in my inputfield instead of the "Some string"
How should i solve this
I am not sure how i overlooked this. But this works:
new_campaign_form = NewCampaignForm(instance=campaign, initial={"tags": "Some String"})
Excuse me, should have looked more and try better

Model form - How to change default ManyToMany widget?

I use Django Model Form:
class Fruit(models.Model):
name = models.CharField(max_length=40)
class Box(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=199)
fruit = models.ManyToManyField(Fruit)
and forms.py:
class BoxModelForm(ModelForm):
class Meta:
model = Box
I have default django ManyToMany widget in form:
http://nov.imghost.us/ly5M.png
How can I change this to input (text type) and if I type into this input:
apple,banana,lemon - comma separated
this Fruit will be created?
As stated here in the documentation :https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#overriding-the-default-fields
You can add a widgets attribute to the Meta of your Modelform to change the default widgets used in the form.
In your case it would be something like this :
class BoxModelForm(ModelForm):
class Meta:
model = Box
widgets = {
'fruit': TheWidgetYouWantToUse(),
}
But actually for the behavior you want to achieve, you could proceed another way.
You should add an extra text field, and write the addition/removal of fruits in the save step, while checking the validity of the differents tags in the clean step.
class BoxModelForm(ModelForm):
fruit_selector = forms.TextField(
max_length=255,
tag = 'Whatever'
)
class Meta:
model = Box
fields = ['user','name']
def clean_fruit_selector(self):
data = self.cleaned_data['fruit_selector']
# Check that data are corrects ie the string is correctly formatted
# If not raise validation error
....
fruit_tags = data.split(",")
#Check that all tags are fruit or raise a validation error
...
return data #or only the list of correct tags
def save(self, commit=True):
instance = super(MyForm, self).save(commit=False)
# Compare the list of tags fruit_tags with self.instance.fruit.all()
....
# Take the right actions
if commit:
instance.save()
return instance
Look into this page for more details on how to change the field validation https://docs.djangoproject.com/en/dev/ref/forms/validation/
This is just a schematic.
django-taggit is a perfect app for this use case.
Define your models like this:
from taggit.managers import TaggableManager
from taggit.models import TagBase, GenericTaggedItemBase
class Fruit(TagBase):
class Meta:
verbose_name = "Fruit"
verbose_name_plural = "Fruits"
class TaggedFruit(GenericTaggedItemBase):
tag = models.ForeignKey(Fruit,
related_name="%(app_label)s_%(class)s_items")
class Box(models.Model):
name = models.CharField(max_length=199)
fruits = TaggableManager(through=TaggedFruit)
Then create basic model form:
class BoxModelForm(ModelForm):
class Meta:
model = Box
And that's it! You can now add fruit tags into your box, separated by comma. In case the fruit doesn't exist, it will be added into Fruit table. Read the docs for more details on how to use django-taggit.
You can use it together with jquery based Selectize.js.